diff --git a/a.txt b/a.txt new file mode 100644 index 00000000000..ee521a7fc28 --- /dev/null +++ b/a.txt @@ -0,0 +1,72 @@ + +> @fern-api/mintlify-importer@0.0.0 test /Users/dsinghvi/Git/fern/packages/cli/docs-importers/mintlify +> vitest --run + + + RUN v2.0.5 /Users/dsinghvi/Git/fern/packages/cli/docs-importers/mintlify + +stdout | src/__test__/migrateFromMintlify.test.ts > add-generator-groups > bland +Converted logo +Converted color configuration + +Bland is a platform for AI phone calling. Using our API, you can easily send or receive phone calls with a programmable voice agent. + +We really care about making our phone calls... + +1. **Fast**: sub-second latency from person speaking to AI responding. +2. **Reliable**: it's our responsibility, day in and day out, to make sure your phone calls work. No exceptions. +3. **Ultra flexible**: configure all aspects of your agent's behavior, by settings it's voice, creating transfer scenarios, configuring the initial greeting, etc. + +# What you can do with Bland + + + + Dispatch AI phone calls to call customers, leads, and to streamline operations. + + + Create inbound phone numbers for customer support, etc. + + + Connect external APIs and take live actions during phone calls. + + + Extract JSON data to answer questions about your calls. + + + Simultaneously send thousands of calls at once. + + + Fine-tune a custom LLM using your enterprise' call recordings and transcripts. + + + +# Getting started + + + + Read the API reference. + + + Learn to send your first phone call and test your agents. + + + Sign up on the developer portal. + + + Learn about enterprise features & meet with a member of the Bland AI team. + + + + + ❯ src/__test__/migrateFromMintlify.test.ts (1 test | 1 failed) 6ms + × add-generator-groups > bland + → Filepath is not relative: /developerportal.png + + Test Files 1 failed (1) + Tests 1 failed (1) + Start at 22:15:44 + Duration 1.34s (transform 447ms, setup 0ms, collect 1.15s, tests 6ms, environment 0ms, prepare 51ms) + +/Users/dsinghvi/Git/fern/packages/cli/docs-importers/mintlify: + ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @fern-api/mintlify-importer@0.0.0 test: `vitest --run` +Exit status 1 diff --git a/fern.schema.json b/fern.schema.json index eb7240f335f..411598d8cc5 100644 --- a/fern.schema.json +++ b/fern.schema.json @@ -373,10 +373,13 @@ { "type": "object", "properties": { + "type": { "type": "string" }, "docs": { "$ref": "#/properties/types/additionalProperties/anyOf/1/anyOf/0/properties/docs" }, - "type": { "type": "string" } + "display-name": { + "$ref": "#/properties/types/additionalProperties/anyOf/2/properties/union/additionalProperties/anyOf/1/properties/display-name" + } }, "required": ["type"], "additionalProperties": false diff --git a/fern/docs.yml b/fern/docs.yml index 7ebf0b85b43..774cef18c38 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -197,7 +197,7 @@ navigation: title: Go slug: go - changelog: ./pages/changelogs/csharp-sdk - title: .Net + title: .NET slug: csharp - changelog: ./pages/changelogs/java-sdk title: Java diff --git a/fern/pages/changelogs/cli/2024-09-27.mdx b/fern/pages/changelogs/cli/2024-09-27.mdx new file mode 100644 index 00000000000..420095bc10b --- /dev/null +++ b/fern/pages/changelogs/cli/2024-09-27.mdx @@ -0,0 +1,6 @@ +## 0.43.6 +**`(fix):`** The OpenAPI importer now appropriately brings in responses that are under the `text/event-stream` +Content-Type if your endpoint is annotated with `x-fern-streaming`. +If your endpoint is not annotated with `x-fern-streaming`, then the response will be ignored. + + diff --git a/fern/pages/changelogs/cli/2024-09-28.mdx b/fern/pages/changelogs/cli/2024-09-28.mdx new file mode 100644 index 00000000000..30ab1df983f --- /dev/null +++ b/fern/pages/changelogs/cli/2024-09-28.mdx @@ -0,0 +1,6 @@ +## 0.43.7 +**`(fix):`** The `valid-markdown` rule has been updated to try and parse the markdown file into a +valid AST. If the file fails to parse, `fern check` will log an error as well +as the path to the markdown. + + diff --git a/fern/pages/changelogs/go-sdk/2024-09-27.mdx b/fern/pages/changelogs/go-sdk/2024-09-27.mdx new file mode 100644 index 00000000000..55f4b4282b6 --- /dev/null +++ b/fern/pages/changelogs/go-sdk/2024-09-27.mdx @@ -0,0 +1,3 @@ +## 0.27.0 +**`(feat):`** Add support for SSE (Server-Sent Events) streaming responses. The user-facing interface for streaming responses remains the same between standard HTTP streaming and SSE. + diff --git a/fern/pages/changelogs/java-sdk/2024-09-26.mdx b/fern/pages/changelogs/java-sdk/2024-09-26.mdx index fd8d5c1801e..41e05e6c38f 100644 --- a/fern/pages/changelogs/java-sdk/2024-09-26.mdx +++ b/fern/pages/changelogs/java-sdk/2024-09-26.mdx @@ -1,4 +1,8 @@ -## 1.2.0 +## 2.2.0 +**`(feat):`** We now provide endpoint methods for streaming byte array requests in addition to the previous methods accepting +byte array directly. + + **`(chore):`** Bump Jackson version to latest (2.17.2) diff --git a/fern/pages/changelogs/python-sdk/2024-09-28.mdx b/fern/pages/changelogs/python-sdk/2024-09-28.mdx new file mode 100644 index 00000000000..df8571729d5 --- /dev/null +++ b/fern/pages/changelogs/python-sdk/2024-09-28.mdx @@ -0,0 +1,11 @@ +## 4.2.7 +**`(fix):`** The generated README will now have a section that links to the generated +SDK Reference (in `reference.md`). + +```md +## Reference + +A full reference for this library can be found [here](./reference.md). +``` + + diff --git a/generators/commons/package.json b/generators/commons/package.json index 6d9f498554b..98dbd8e65a1 100644 --- a/generators/commons/package.json +++ b/generators/commons/package.json @@ -32,7 +32,7 @@ "@fern-api/fs-utils": "workspace:*", "@fern-api/logger": "workspace:*", "@fern-api/logging-execa": "workspace:*", - "@fern-fern/generator-cli-sdk": "0.0.56", + "@fern-fern/generator-cli-sdk": "0.0.17", "@fern-fern/generator-exec-sdk": "^0.0.898", "js-yaml": "^4.1.0", "lodash-es": "^4.17.21", diff --git a/generators/csharp/sdk/package.json b/generators/csharp/sdk/package.json index 3a06c5eb433..3f3824299b3 100644 --- a/generators/csharp/sdk/package.json +++ b/generators/csharp/sdk/package.json @@ -38,7 +38,7 @@ "@fern-api/fs-utils": "workspace:*", "@fern-api/generator-commons": "workspace:*", "@fern-api/logger": "workspace:*", - "@fern-fern/generator-cli-sdk": "0.0.56", + "@fern-fern/generator-cli-sdk": "0.0.17", "@fern-fern/generator-exec-sdk": "^0.0.898", "url-join": "^5.0.0", "@fern-fern/ir-sdk": "^53.7.0", diff --git a/generators/go/internal/generator/generator.go b/generators/go/internal/generator/generator.go index 86a8b1173ed..6bf886241ad 100644 --- a/generators/go/internal/generator/generator.go +++ b/generators/go/internal/generator/generator.go @@ -1547,3 +1547,12 @@ var pointerFunctionNames = map[string]struct{}{ "Uintptr": struct{}{}, "Time": struct{}{}, } + +// valueOf dereferences the given value, or returns the zero value if nil. +func valueOf[T any](value *T) T { + var result T + if value == nil { + return result + } + return *value +} diff --git a/generators/go/internal/generator/sdk.go b/generators/go/internal/generator/sdk.go index beaaa36778d..4f102ab58b6 100644 --- a/generators/go/internal/generator/sdk.go +++ b/generators/go/internal/generator/sdk.go @@ -1038,6 +1038,9 @@ func (f *fileWriter) WriteClient( } } } + if endpoint.Accept != "" { + f.P(fmt.Sprintf(`%s.Set("Accept", %q)`, headersParameter, endpoint.Accept)) + } if endpoint.ContentType != "" { f.P(fmt.Sprintf(`%s.Set("Content-Type", %q)`, headersParameter, endpoint.ContentType)) } @@ -1114,7 +1117,7 @@ func (f *fileWriter) WriteClient( } // Prepare a response variable. - if endpoint.ResponseType != "" && !endpoint.IsStreaming && endpoint.PaginationInfo == nil { + if endpoint.ResponseType != "" && endpoint.StreamingInfo == nil && endpoint.PaginationInfo == nil { f.P(fmt.Sprintf(endpoint.ResponseInitializerFormat, endpoint.ResponseType)) } @@ -1208,13 +1211,23 @@ func (f *fileWriter) WriteClient( } // Issue the request. - if endpoint.IsStreaming { + if endpoint.StreamingInfo != nil { + streamingInfo := endpoint.StreamingInfo f.P("streamer := core.NewStreamer[", endpoint.ResponseType, "](", receiver, ".caller)") f.P("return streamer.Stream(") f.P("ctx,") f.P("&core.StreamParams{") f.P("URL: endpointURL, ") f.P("Method:", endpoint.Method, ",") + if streamingInfo.Delimiter != "" { + f.P("Delimiter: ", streamingInfo.Delimiter, ",") + } + if streamingInfo.Prefix != "" { + f.P("Prefix:", streamingInfo.Prefix, ",") + } + if streamingInfo.Terminator != "" { + f.P("Terminator:", streamingInfo.Terminator, ",") + } f.P("MaxAttempts: options.MaxAttempts,") f.P("BodyProperties: options.BodyProperties,") f.P("QueryParameters: options.QueryParameters,") @@ -1226,9 +1239,6 @@ func (f *fileWriter) WriteClient( if endpoint.ErrorDecoderParameterName != "" { f.P("ErrorDecoder:", endpoint.ErrorDecoderParameterName, ",") } - if endpoint.StreamDelimiter != "" { - f.P("Delimiter: ", endpoint.StreamDelimiter, ",") - } f.P("},") f.P(")") f.P("}") @@ -1390,6 +1400,48 @@ func (f *fileWriter) WriteClient( ) } +type streamingInfo struct { + Delimiter string + Prefix string + Terminator string + AcceptHeader string +} + +func getStreamingInfo( + irEndpoint *ir.HttpEndpoint, +) (*streamingInfo, error) { + if irEndpoint == nil || irEndpoint.Response == nil || irEndpoint.Response.Streaming == nil { + return nil, nil + } + streamingResponse := irEndpoint.Response.Streaming + switch streamingResponse.Type { + case "text": + return &streamingInfo{}, nil + case "json": + var terminator string + if value := valueOf(streamingResponse.Json.Terminator); value != "" { + terminator = fmt.Sprintf("%q", value) + } + return &streamingInfo{ + Terminator: terminator, + }, nil + case "sse": + terminator := valueOf(streamingResponse.Sse.Terminator) + if terminator != "" { + terminator = fmt.Sprintf("%q", terminator) + } else { + terminator = "core.DefaultSSETerminator" + } + return &streamingInfo{ + Prefix: "core.DefaultSSEDataPrefix", + Terminator: terminator, + AcceptHeader: "text/event-stream", + }, nil + default: + return nil, fmt.Errorf("stream response type %q is not supported", streamingResponse.Type) + } +} + type paginationInfo struct { Type string Page *ir.QueryParameter @@ -1996,10 +2048,9 @@ type endpoint struct { OptionConstructor string PathSuffix string Method string - IsStreaming bool - StreamDelimiter string ErrorDecoderParameterName string Idempotent bool + Accept string ContentType string Errors ir.ResponseErrors QueryParameters []*ir.QueryParameter @@ -2008,6 +2059,7 @@ type endpoint struct { FilePropertyInfo *filePropertyInfo FileProperties []*ir.FileProperty FileBodyProperties []*ir.InlinedRequestBodyProperty + StreamingInfo *streamingInfo PaginationInfo *paginationInfo } @@ -2172,6 +2224,11 @@ func (f *fileWriter) endpointFromIR( }, ) + streamingInfo, err := getStreamingInfo(irEndpoint) + if err != nil { + return nil, err + } + paginationInfo, err := f.getPaginationInfo(irEndpoint, scope, requestParameterName) if err != nil { return nil, err @@ -2185,8 +2242,6 @@ func (f *fileWriter) endpointFromIR( signatureReturnValues string successfulReturnValues string errorReturnValues string - streamDelimiter string - isStreaming bool ) var responseIsOptionalParameter bool if irEndpoint.Response != nil { @@ -2234,12 +2289,6 @@ func (f *fileWriter) endpointFromIR( successfulReturnValues = "response.String(), nil" errorReturnValues = `"", err` case "streaming": - if irEndpoint.Response.Streaming.Json == nil && irEndpoint.Response.Streaming.Text == nil { - return nil, fmt.Errorf("unsupported streaming response type: %s", irEndpoint.Response.Streaming.Type) - } - if irEndpoint.Response.Streaming.Json != nil && irEndpoint.Response.Streaming.Json.Terminator != nil { - streamDelimiter = *irEndpoint.Response.Streaming.Json.Terminator - } typeReference, err := typeReferenceFromStreamingResponse(irEndpoint.Response.Streaming) if err != nil { return nil, err @@ -2248,7 +2297,6 @@ func (f *fileWriter) endpointFromIR( responseParameterName = "response" signatureReturnValues = fmt.Sprintf("(*core.Stream[%s], error)", responseType) errorReturnValues = "nil, err" - isStreaming = true default: return nil, fmt.Errorf("%s requests are not supported yet", irEndpoint.Response.Type) } @@ -2308,6 +2356,11 @@ func (f *fileWriter) endpointFromIR( optionConstructor = "core.NewIdempotentRequestOptions(opts...)" } + var accept string + if streamingInfo != nil && streamingInfo.AcceptHeader != "" { + accept = streamingInfo.AcceptHeader + } + contentTypeOverride := contentTypeFromRequestBody(irEndpoint.RequestBody) if contentTypeOverride != "" { contentType = contentTypeOverride @@ -2336,9 +2389,8 @@ func (f *fileWriter) endpointFromIR( BaseURL: baseURL, PathSuffix: pathSuffix, Method: irMethodToMethodEnum(irEndpoint.Method), - IsStreaming: isStreaming, - StreamDelimiter: streamDelimiter, ErrorDecoderParameterName: errorDecoderParameterName, + Accept: accept, ContentType: contentType, Idempotent: irEndpoint.Idempotent, Errors: irEndpoint.Errors, @@ -2348,6 +2400,7 @@ func (f *fileWriter) endpointFromIR( FilePropertyInfo: filePropertyInfo, FileProperties: fileProperties, FileBodyProperties: fileBodyProperties, + StreamingInfo: streamingInfo, PaginationInfo: paginationInfo, }, nil } @@ -3109,6 +3162,8 @@ func typeReferenceFromStreamingResponse( switch streamingResponse.Type { case "json": return streamingResponse.Json.Payload, nil + case "sse": + return streamingResponse.Sse.Payload, nil case "text": return ir.NewTypeReferenceFromPrimitive(ir.PrimitiveTypeString), nil } diff --git a/generators/go/internal/generator/sdk/core/stream.go b/generators/go/internal/generator/sdk/core/stream.go index c610ad3963e..30e374dc4e2 100644 --- a/generators/go/internal/generator/sdk/core/stream.go +++ b/generators/go/internal/generator/sdk/core/stream.go @@ -10,7 +10,16 @@ import ( "strings" ) -const defaultStreamDelimiter = '\n' +const ( + // DefaultDataPrefix is the default prefix used for SSE streaming. + DefaultSSEDataPrefix = "data: " + + // DefaultTerminator is the default terminator used for SSE streaming. + DefaultSSETerminator = "[DONE]" + + // The default stream delimiter used to split messages. + defaultStreamDelimiter = '\n' +) // Streamer calls APIs and streams responses using a *Stream. type Streamer[T any] struct { @@ -30,7 +39,9 @@ func NewStreamer[T any](caller *Caller) *Streamer[T] { type StreamParams struct { URL string Method string + Prefix string Delimiter string + Terminator string MaxAttempts uint Headers http.Header BodyProperties map[string]interface{} @@ -97,6 +108,13 @@ func (s *Streamer[T]) Stream(ctx context.Context, params *StreamParams) (*Stream if params.Delimiter != "" { opts = append(opts, WithDelimiter(params.Delimiter)) } + if params.Prefix != "" { + opts = append(opts, WithPrefix(params.Prefix)) + } + if params.Terminator != "" { + opts = append(opts, WithTerminator(params.Terminator)) + } + return NewStream[T](resp, opts...), nil } @@ -118,6 +136,24 @@ func WithDelimiter(delimiter string) StreamOption { } } +// WithPrefix overrides the prefix for the Stream. +// +// By default, the Stream doesn't have a prefix. +func WithPrefix(prefix string) StreamOption { + return func(opts *streamOptions) { + opts.prefix = prefix + } +} + +// WithTerminator overrides the terminator for the Stream. +// +// By default, the Stream terminates on EOF. +func WithTerminator(terminator string) StreamOption { + return func(opts *streamOptions) { + opts.terminator = terminator + } +} + // NewStream constructs a new Stream from the given *http.Response. func NewStream[T any](response *http.Response, opts ...StreamOption) *Stream[T] { options := new(streamOptions) @@ -125,7 +161,7 @@ func NewStream[T any](response *http.Response, opts ...StreamOption) *Stream[T] opt(options) } return &Stream[T]{ - reader: newStreamReader(response.Body, options.delimiter), + reader: newStreamReader(response.Body, options), closer: response.Body, } } @@ -160,9 +196,12 @@ type streamReader interface { // By default, the streamReader uses a simple a *bufio.Reader // which splits on newlines, and otherwise use a *bufio.Scanner to // split on custom delimiters. -func newStreamReader(reader io.Reader, delimiter string) streamReader { - if len(delimiter) > 0 { - return newScannerStreamReader(reader, delimiter) +func newStreamReader(reader io.Reader, options *streamOptions) streamReader { + if !options.isEmpty() { + if options.delimiter == "" { + options.delimiter = string(defaultStreamDelimiter) + } + return newScannerStreamReader(reader, options) } return newBufferStreamReader(reader) } @@ -187,37 +226,69 @@ func (b *bufferStreamReader) ReadFromStream() ([]byte, error) { // configurable delimiters. type scannerStreamReader struct { scanner *bufio.Scanner + options *streamOptions } -func newScannerStreamReader(reader io.Reader, delimiter string) *scannerStreamReader { +func newScannerStreamReader( + reader io.Reader, + options *streamOptions, +) *scannerStreamReader { scanner := bufio.NewScanner(reader) - scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) { - if atEOF && len(data) == 0 { + stream := &scannerStreamReader{ + scanner: scanner, + options: options, + } + scanner.Split(func(bytes []byte, atEOF bool) (int, []byte, error) { + if atEOF && len(bytes) == 0 { return 0, nil, nil } - if i := strings.Index(string(data), delimiter); i >= 0 { - return i + len(delimiter), data[0:i], nil - } - if atEOF { - return len(data), data, nil + n, data, err := stream.parse(bytes) + if stream.isTerminated(data) { + return 0, nil, io.EOF } - return 0, nil, nil + return n, data, err }) - return &scannerStreamReader{ - scanner: scanner, - } + return stream } -func (b *scannerStreamReader) ReadFromStream() ([]byte, error) { - if b.scanner.Scan() { - return b.scanner.Bytes(), nil +func (s *scannerStreamReader) ReadFromStream() ([]byte, error) { + if s.scanner.Scan() { + return s.scanner.Bytes(), nil } - if err := b.scanner.Err(); err != nil { + if err := s.scanner.Err(); err != nil { return nil, err } return nil, io.EOF } +func (s *scannerStreamReader) parse(bytes []byte) (int, []byte, error) { + var start int + if s.options != nil && s.options.prefix != "" { + if i := strings.Index(string(bytes), s.options.prefix); i >= 0 { + start = i + len(s.options.prefix) + } + } + data := bytes[start:] + if i := strings.Index(string(data), s.options.delimiter); i >= 0 { + data = data[:i+len(s.options.delimiter)] + } + n := start + len(data) + len(s.options.delimiter) + return n, data, nil +} + +func (s *scannerStreamReader) isTerminated(bytes []byte) bool { + if s.options == nil || s.options.terminator == "" { + return false + } + return strings.Contains(string(bytes), s.options.terminator) +} + type streamOptions struct { - delimiter string + delimiter string + prefix string + terminator string +} + +func (s *streamOptions) isEmpty() bool { + return s.delimiter == "" && s.prefix == "" && s.terminator == "" } diff --git a/generators/go/sdk/versions.yml b/generators/go/sdk/versions.yml index d0b803dff11..9da459e2a9e 100644 --- a/generators/go/sdk/versions.yml +++ b/generators/go/sdk/versions.yml @@ -1,3 +1,11 @@ +- version: 0.27.0 + changelogEntry: + - type: feat + summary: >- + Add support for SSE (Server-Sent Events) streaming responses. The user-facing + interface for streaming responses remains the same between standard HTTP + streaming and SSE. + irVersion: 40 - version: 0.26.0 changelogEntry: - type: feat diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/Cli.java b/generators/java/sdk/src/main/java/com/fern/java/client/Cli.java index d4b66d9a284..0fb72f3aee2 100644 --- a/generators/java/sdk/src/main/java/com/fern/java/client/Cli.java +++ b/generators/java/sdk/src/main/java/com/fern/java/client/Cli.java @@ -19,6 +19,8 @@ import com.fern.java.client.generators.CoreMediaTypesGenerator; import com.fern.java.client.generators.EnvironmentGenerator; import com.fern.java.client.generators.ErrorGenerator; +import com.fern.java.client.generators.FileStreamGenerator; +import com.fern.java.client.generators.InputStreamRequestBodyGenerator; import com.fern.java.client.generators.OAuthTokenSupplierGenerator; import com.fern.java.client.generators.RequestOptionsGenerator; import com.fern.java.client.generators.ResponseBodyInputStreamGenerator; @@ -198,6 +200,12 @@ public GeneratedRootClient generateClient( new ResponseBodyInputStreamGenerator(context); this.addGeneratedFile(responseBodyInputStreamGenerator.generateFile()); + InputStreamRequestBodyGenerator inputStreamRequestBodyGenerator = new InputStreamRequestBodyGenerator(context); + this.addGeneratedFile(inputStreamRequestBodyGenerator.generateFile()); + + FileStreamGenerator fileStreamGenerator = new FileStreamGenerator(context); + this.addGeneratedFile(fileStreamGenerator.generateFile()); + ResponseBodyReaderGenerator responseBodyReaderGenerator = new ResponseBodyReaderGenerator(context); this.addGeneratedFile(responseBodyReaderGenerator.generateFile()); diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/ClientPoetClassNameFactory.java b/generators/java/sdk/src/main/java/com/fern/java/client/ClientPoetClassNameFactory.java index bf131363b24..9b09c32a987 100644 --- a/generators/java/sdk/src/main/java/com/fern/java/client/ClientPoetClassNameFactory.java +++ b/generators/java/sdk/src/main/java/com/fern/java/client/ClientPoetClassNameFactory.java @@ -24,6 +24,14 @@ public ClassName getErrorClassName(ErrorDeclaration errorDeclaration) { errorDeclaration.getName().getName().getPascalCase().getSafeName()); } + public ClassName getInputStreamRequestBodyClassName() { + return ClassName.get(getCorePackage(), "InputStreamRequestBody"); + } + + public ClassName getFileStreamClassName() { + return ClassName.get(getCorePackage(), "FileStream"); + } + public ClassName getRetryInterceptorClassName() { return ClassName.get(getCorePackage(), "RetryInterceptor"); } diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/generators/ClientGeneratorUtils.java b/generators/java/sdk/src/main/java/com/fern/java/client/generators/ClientGeneratorUtils.java index 75d3fd29e73..2948d43e48a 100644 --- a/generators/java/sdk/src/main/java/com/fern/java/client/generators/ClientGeneratorUtils.java +++ b/generators/java/sdk/src/main/java/com/fern/java/client/generators/ClientGeneratorUtils.java @@ -118,6 +118,17 @@ public Result buildClients() { } implBuilder.addMethod(httpEndpointMethodSpecs.getNonRequestOptionsMethodSpec()); implBuilder.addMethod(httpEndpointMethodSpecs.getRequestOptionsMethodSpec()); + if (httpEndpointMethodSpecs + .getNonRequestOptionsByteArrayMethodSpec() + .isPresent()) { + implBuilder.addMethod(httpEndpointMethodSpecs + .getNonRequestOptionsByteArrayMethodSpec() + .get()); + } + if (httpEndpointMethodSpecs.getByteArrayMethodSpec().isPresent()) { + implBuilder.addMethod( + httpEndpointMethodSpecs.getByteArrayMethodSpec().get()); + } generatedWrappedRequests.addAll(httpEndpointMethodSpecFactory.getGeneratedWrappedRequests()); } } diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/generators/FileStreamGenerator.java b/generators/java/sdk/src/main/java/com/fern/java/client/generators/FileStreamGenerator.java new file mode 100644 index 00000000000..870e925f911 --- /dev/null +++ b/generators/java/sdk/src/main/java/com/fern/java/client/generators/FileStreamGenerator.java @@ -0,0 +1,44 @@ +/* + * (c) Copyright 2023 Birch Solutions Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fern.java.client.generators; + +import com.fern.java.client.ClientGeneratorContext; +import com.fern.java.generators.AbstractFileGenerator; +import com.fern.java.output.GeneratedResourcesJavaFile; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public final class FileStreamGenerator extends AbstractFileGenerator { + + public FileStreamGenerator(ClientGeneratorContext clientGeneratorContext) { + super(clientGeneratorContext.getPoetClassNameFactory().getFileStreamClassName(), clientGeneratorContext); + } + + @Override + public GeneratedResourcesJavaFile generateFile() { + try (InputStream is = FileStreamGenerator.class.getResourceAsStream("/FileStream.java")) { + String contents = new String(is.readAllBytes(), StandardCharsets.UTF_8); + return GeneratedResourcesJavaFile.builder() + .className(className) + .contents(contents) + .build(); + } catch (IOException e) { + throw new RuntimeException("Failed to read FileStream.java"); + } + } +} diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/generators/InputStreamRequestBodyGenerator.java b/generators/java/sdk/src/main/java/com/fern/java/client/generators/InputStreamRequestBodyGenerator.java new file mode 100644 index 00000000000..957eaa65dcf --- /dev/null +++ b/generators/java/sdk/src/main/java/com/fern/java/client/generators/InputStreamRequestBodyGenerator.java @@ -0,0 +1,47 @@ +/* + * (c) Copyright 2023 Birch Solutions Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.fern.java.client.generators; + +import com.fern.java.client.ClientGeneratorContext; +import com.fern.java.generators.AbstractFileGenerator; +import com.fern.java.output.GeneratedResourcesJavaFile; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public final class InputStreamRequestBodyGenerator extends AbstractFileGenerator { + + public InputStreamRequestBodyGenerator(ClientGeneratorContext clientGeneratorContext) { + super( + clientGeneratorContext.getPoetClassNameFactory().getInputStreamRequestBodyClassName(), + clientGeneratorContext); + } + + @Override + public GeneratedResourcesJavaFile generateFile() { + try (InputStream is = + InputStreamRequestBodyGenerator.class.getResourceAsStream("/InputStreamRequestBody.java")) { + String contents = new String(is.readAllBytes(), StandardCharsets.UTF_8); + return GeneratedResourcesJavaFile.builder() + .className(className) + .contents(contents) + .build(); + } catch (IOException e) { + throw new RuntimeException("Failed to read InputStreamRequestBody.java"); + } + } +} diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/AbstractEndpointWriter.java b/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/AbstractEndpointWriter.java index 629a7befa3b..4448df9d75c 100644 --- a/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/AbstractEndpointWriter.java +++ b/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/AbstractEndpointWriter.java @@ -73,6 +73,7 @@ import com.fern.java.output.GeneratedObjectMapper; import com.fern.java.utils.JavaDocUtils; import com.fern.java.utils.TypeReferenceUtils.ContainerTypeToUnderlyingType; +import com.squareup.javapoet.ArrayTypeName; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.CodeBlock.Builder; @@ -81,6 +82,7 @@ import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -194,7 +196,7 @@ public final HttpEndpointMethodSpecs generate() { // Step 2: Add additional parameters List additionalParameters = additionalParameters(); - // Step 3: Add path parameters + // Step 3: Add parameters endpointMethodBuilder.addParameters(pathParameters); endpointMethodBuilder.addParameters(additionalParameters); if (httpEndpoint.getIdempotent()) { @@ -203,10 +205,7 @@ public final HttpEndpointMethodSpecs generate() { REQUEST_OPTIONS_PARAMETER_NAME) .build()); } else { - endpointMethodBuilder.addParameter(ParameterSpec.builder( - clientGeneratorContext.getPoetClassNameFactory().getRequestOptionsClassName(), - REQUEST_OPTIONS_PARAMETER_NAME) - .build()); + endpointMethodBuilder.addParameter(requestOptionsParameterSpec()); } // Step 4: Get http client initializer @@ -309,9 +308,53 @@ public final HttpEndpointMethodSpecs generate() { bodyParameterSpec.type) .build(); } + Optional maybeBytes = httpEndpoint + .getSdkRequest() + .flatMap( + sdkRequest -> sdkRequest.getShape().getJustRequestBody().flatMap(SdkRequestBodyType::getBytes)); + MethodSpec nonRequestOptionsByteArrayMethodSpec = null; + MethodSpec byteArrayMethodSpec = null; + + // add direct byte array endpoints for backwards compatibility + if (maybeBytes.isPresent()) { + BytesRequest bytes = maybeBytes.get(); + ParameterSpec requestParameterSpec = + getBytesRequestParameterSpec(bytes, sdkRequest().get(), ArrayTypeName.of(byte.class)); + MethodSpec byteArrayBaseMethodSpec = MethodSpec.methodBuilder(endpointWithRequestOptions.name) + .addModifiers(Modifier.PUBLIC) + .addParameters(pathParameters) + .addParameters(List.of(requestParameterSpec)) + .addJavadoc(endpointWithRequestOptions.javadoc) + .returns(endpointWithRequestOptions.returnType) + .build(); + Builder methodBodyBuilder = CodeBlock.builder(); + if (!byteArrayBaseMethodSpec.returnType.equals(TypeName.VOID)) { + methodBodyBuilder.add("return "); + } + CodeBlock baseMethodBody = methodBodyBuilder + .add( + "$L(new $T($L)", + endpointWithRequestOptions.name, + ByteArrayInputStream.class, + requestParameterSpec.name) + .build(); + nonRequestOptionsByteArrayMethodSpec = byteArrayBaseMethodSpec.toBuilder() + .addStatement(baseMethodBody.toBuilder().add(")").build()) + .build(); + byteArrayMethodSpec = byteArrayBaseMethodSpec.toBuilder() + .addParameter(requestOptionsParameterSpec()) + .addStatement(baseMethodBody.toBuilder() + .add(", $L)", REQUEST_OPTIONS_PARAMETER_NAME) + .build()) + .build(); + } return new HttpEndpointMethodSpecs( - endpointWithRequestOptions, endpointWithoutRequestOptions, endpointWithoutRequest); + endpointWithRequestOptions, + endpointWithoutRequestOptions, + endpointWithoutRequest, + byteArrayMethodSpec, + nonRequestOptionsByteArrayMethodSpec); } public abstract Optional sdkRequest(); @@ -330,6 +373,24 @@ public abstract CodeBlock getInitializeRequestCodeBlock( CodeBlock inlineableHttpUrl, boolean sendContentType); + public final ParameterSpec requestOptionsParameterSpec() { + return ParameterSpec.builder( + clientGeneratorContext.getPoetClassNameFactory().getRequestOptionsClassName(), + REQUEST_OPTIONS_PARAMETER_NAME) + .build(); + } + + protected final ParameterSpec getBytesRequestParameterSpec( + BytesRequest bytes, SdkRequest sdkRequest, TypeName typeName) { + if (bytes.getIsOptional()) { + typeName = ParameterizedTypeName.get(ClassName.get(Optional.class), typeName); + } + return ParameterSpec.builder( + typeName, + sdkRequest.getRequestParameterName().getCamelCase().getSafeName()) + .build(); + } + public final CodeBlock getResponseParserCodeBlock() { CodeBlock.Builder httpResponseBuilder = CodeBlock.builder() // Default the request client diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/HttpEndpointMethodSpecs.java b/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/HttpEndpointMethodSpecs.java index 60038a584c1..8a8e0aaa70d 100644 --- a/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/HttpEndpointMethodSpecs.java +++ b/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/HttpEndpointMethodSpecs.java @@ -24,14 +24,20 @@ public final class HttpEndpointMethodSpecs { private final MethodSpec nonRequestOptionsMethodSpec; private final MethodSpec requestOptionsMethodSpec; private final MethodSpec noRequestBodyMethodSpec; + private final MethodSpec byteArrayMethodSpec; + private final MethodSpec nonRequestOptionsByteArrayMethodSpec; public HttpEndpointMethodSpecs( MethodSpec requestOptionsMethodSpec, MethodSpec nonRequestOptionsMethodSpec, - MethodSpec noRequestBodyMethodSpec) { + MethodSpec noRequestBodyMethodSpec, + MethodSpec byteArrayMethodSpec, + MethodSpec nonRequestOptionsByteArrayMethodSpec) { this.nonRequestOptionsMethodSpec = nonRequestOptionsMethodSpec; this.requestOptionsMethodSpec = requestOptionsMethodSpec; this.noRequestBodyMethodSpec = noRequestBodyMethodSpec; + this.byteArrayMethodSpec = byteArrayMethodSpec; + this.nonRequestOptionsByteArrayMethodSpec = nonRequestOptionsByteArrayMethodSpec; } public MethodSpec getNonRequestOptionsMethodSpec() { @@ -45,4 +51,12 @@ public MethodSpec getRequestOptionsMethodSpec() { public Optional getNoRequestBodyMethodSpec() { return Optional.ofNullable(noRequestBodyMethodSpec); } + + public Optional getByteArrayMethodSpec() { + return Optional.ofNullable(byteArrayMethodSpec); + } + + public Optional getNonRequestOptionsByteArrayMethodSpec() { + return Optional.ofNullable(nonRequestOptionsByteArrayMethodSpec); + } } diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/OnlyRequestEndpointWriter.java b/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/OnlyRequestEndpointWriter.java index 9e7c33c2018..5434d9c69eb 100644 --- a/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/OnlyRequestEndpointWriter.java +++ b/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/OnlyRequestEndpointWriter.java @@ -34,18 +34,17 @@ import com.fern.java.generators.object.EnrichedObjectProperty; import com.fern.java.output.GeneratedJavaFile; import com.fern.java.output.GeneratedObjectMapper; -import com.squareup.javapoet.ArrayTypeName; -import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.ParameterSpec; -import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; +import java.io.InputStream; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import okhttp3.Headers; +import okhttp3.MediaType; import okhttp3.Request; import okhttp3.RequestBody; @@ -149,17 +148,7 @@ public ParameterSpec visitTypeReference(HttpRequestBodyReference typeReference) @Override public ParameterSpec visitBytes(BytesRequest bytes) { - TypeName typeName = ArrayTypeName.of(byte.class); - if (bytes.getIsOptional()) { - typeName = ParameterizedTypeName.get(ClassName.get(Optional.class), typeName); - } - return ParameterSpec.builder( - typeName, - sdkRequest - .getRequestParameterName() - .getCamelCase() - .getSafeName()) - .build(); + return getBytesRequestParameterSpec(bytes, sdkRequest, TypeName.get(InputStream.class)); } @Override @@ -212,10 +201,6 @@ public Void visitTypeReference(HttpRequestBodyReference typeReference) { @Override public Void visitBytes(BytesRequest bytes) { - builder.add( - ".addHeader($S, $S)\n", - AbstractEndpointWriter.CONTENT_TYPE_HEADER, - bytes.getContentType().orElse(AbstractEndpointWriter.APPLICATION_OCTET_STREAM)); return null; } @@ -304,10 +289,12 @@ public Void visitTypeReference(HttpRequestBodyReference _typeReference) { @Override public Void visitBytes(BytesRequest bytes) { codeBlock.addStatement( - "$T $L = $T.create($L)", + "$T $L = new $T($T.parse($S), $L)", RequestBody.class, getOkhttpRequestBodyName(), - RequestBody.class, + clientGeneratorContext.getPoetClassNameFactory().getInputStreamRequestBodyClassName(), + MediaType.class, + bytes.getContentType().orElse(APPLICATION_OCTET_STREAM), sdkRequest.getRequestParameterName().getCamelCase().getSafeName()); return null; } diff --git a/generators/java/sdk/src/main/resources/FileStream.java b/generators/java/sdk/src/main/resources/FileStream.java new file mode 100644 index 00000000000..89cc50fe0f3 --- /dev/null +++ b/generators/java/sdk/src/main/resources/FileStream.java @@ -0,0 +1,55 @@ +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} \ No newline at end of file diff --git a/generators/java/sdk/src/main/resources/InputStreamRequestBody.java b/generators/java/sdk/src/main/resources/InputStreamRequestBody.java new file mode 100644 index 00000000000..e125595a22d --- /dev/null +++ b/generators/java/sdk/src/main/resources/InputStreamRequestBody.java @@ -0,0 +1,74 @@ +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} \ No newline at end of file diff --git a/generators/java/sdk/versions.yml b/generators/java/sdk/versions.yml index ec7122a01b2..951546bc746 100644 --- a/generators/java/sdk/versions.yml +++ b/generators/java/sdk/versions.yml @@ -1,10 +1,14 @@ - changelogEntry: + - summary: | + We now provide endpoint methods for streaming byte array requests in addition to the previous methods accepting + byte array directly. + type: feat - summary: | Bump Jackson version to latest (2.17.2) type: chore createdAt: '2024-09-26' irVersion: 46 - version: 1.2.0 + version: 2.2.0 - changelogEntry: - summary: | diff --git a/generators/python/poetry.lock b/generators/python/poetry.lock index 217e3204a8e..82e3d2c82cd 100644 --- a/generators/python/poetry.lock +++ b/generators/python/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -204,19 +204,18 @@ reference = "fern-prod" [[package]] name = "fern-fern-generator-cli-sdk" -version = "0.0.59" +version = "0.0.17" description = "" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "fern_fern_generator_cli_sdk-0.0.59-py3-none-any.whl", hash = "sha256:daddbc4b48585f0ad0bd3251b6f045e74fbcb83bcdfdd7b9b01b7df03ab29763"}, - {file = "fern_fern_generator_cli_sdk-0.0.59.tar.gz", hash = "sha256:7d88692519432a4f551e1125b2ca70cdb539a7d43177f2e018cb492a01b716bd"}, + {file = "fern_fern_generator_cli_sdk-0.0.17-py3-none-any.whl", hash = "sha256:b5cc5c29dbdfb1d1569c5006aad94e49d36b9a6de92c2678c7db3895c8f037e2"}, + {file = "fern_fern_generator_cli_sdk-0.0.17.tar.gz", hash = "sha256:abe3fda300e0cc52513811d70255f298a721036c267277ba147f7948de1d8b3c"}, ] [package.dependencies] httpx = ">=0.21.2" pydantic = ">=1.9.2" -pydantic-core = ">=2.18.2,<3.0.0" typing_extensions = ">=4.0.0" [package.source] @@ -1058,4 +1057,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "afdb8556cad38c3df3faef1c8a2a98548ec2269e3a539bf570b7718fa975d380" +content-hash = "6f8243a7f89479c2cf0e97888c0651b554d717a20d6e3d8b1f75e978495b0594" diff --git a/generators/python/pyproject.toml b/generators/python/pyproject.toml index 333ec7782d0..bc62fd8adc3 100644 --- a/generators/python/pyproject.toml +++ b/generators/python/pyproject.toml @@ -12,7 +12,7 @@ fern-fern-generator-exec-sdk = {version = "0.82.5", source = "fern-prod"} ordered-set = "^4.1.0" pydantic-core = "^2.18.2" fern-fern-fdr-sdk = {version = "0.98.20", source = "fern-prod"} -fern-fern-generator-cli-sdk = {version = "0.0.59", source = "fern-prod"} +fern-fern-generator-cli-sdk = {version = "0.0.17", source = "fern-prod"} fern_fern_ir_v53 = "53.12.0" [tool.poetry.dev-dependencies] diff --git a/generators/python/sdk/versions.yml b/generators/python/sdk/versions.yml index 7ffabf3be33..441658d8188 100644 --- a/generators/python/sdk/versions.yml +++ b/generators/python/sdk/versions.yml @@ -1,4 +1,18 @@ # For unreleased changes, use unreleased.yml +- version: 4.2.7 + irVersion: 53 + changelogEntry: + - type: fix + summary: | + The generated README will now have a section that links to the generated + SDK Reference (in `reference.md`). + + ```md + ## Reference + + A full reference for this library can be found [here](./reference.md). + ``` + - version: 4.2.7-rc4 irVersion: 53 changelogEntry: diff --git a/generators/python/src/fern_python/generator_cli/generator_cli.py b/generators/python/src/fern_python/generator_cli/generator_cli.py index b2df1968499..244d9df4625 100644 --- a/generators/python/src/fern_python/generator_cli/generator_cli.py +++ b/generators/python/src/fern_python/generator_cli/generator_cli.py @@ -178,6 +178,7 @@ def _get_readme_config( api_reference_link=self._ir.readme_config.api_reference_link if self._ir.readme_config else None, banner_link=self._ir.readme_config.banner_link if self._ir.readme_config else None, features=features, + reference_markdown_path=f"./{REFERENCE_FILENAME}" ) def _read_feature_config(self) -> generatorcli.feature.FeatureConfig: diff --git a/generators/typescript/sdk/CHANGELOG.md b/generators/typescript/sdk/CHANGELOG.md index c90514c8f15..0ffec774d58 100644 --- a/generators/typescript/sdk/CHANGELOG.md +++ b/generators/typescript/sdk/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.40.7] - 2024-09-28 + +- Fix: The generated README will now have a section that links to the generated + SDK Reference (in `reference.md`). + + ```md + ## Reference + + A full reference for this library can be found [here](./reference.md). + ``` + +## [0.40.6] - 2024-09-18 + +- Fix: The TypeScript SDK now supports specifying a custom contentType if one is specified. + ## [0.40.5] - 2024-09-18 - Fix: The snippet templates for file upload are now accurate and also respect the feature diff --git a/generators/typescript/sdk/VERSION b/generators/typescript/sdk/VERSION index d3568f30dc8..7754b3ee7fd 100644 --- a/generators/typescript/sdk/VERSION +++ b/generators/typescript/sdk/VERSION @@ -1 +1 @@ -0.40.5 +0.40.7 diff --git a/generators/typescript/sdk/client-class-generator/src/endpoint-request/GeneratedDefaultEndpointRequest.ts b/generators/typescript/sdk/client-class-generator/src/endpoint-request/GeneratedDefaultEndpointRequest.ts index 1801367e47a..47cfde2d95c 100644 --- a/generators/typescript/sdk/client-class-generator/src/endpoint-request/GeneratedDefaultEndpointRequest.ts +++ b/generators/typescript/sdk/client-class-generator/src/endpoint-request/GeneratedDefaultEndpointRequest.ts @@ -181,7 +181,7 @@ export class GeneratedDefaultEndpointRequest implements GeneratedEndpointRequest headers: this.getHeaders(context), queryParameters: this.queryParams.getReferenceTo(context), body: this.getSerializedRequestBodyWithNullCheck(context), - contentType: "application/json", + contentType: this.requestBody?.contentType ?? "application/json", requestType: "json" }; } diff --git a/generators/typescript/sdk/generator/package.json b/generators/typescript/sdk/generator/package.json index 6681607d87e..dd61672d0ce 100644 --- a/generators/typescript/sdk/generator/package.json +++ b/generators/typescript/sdk/generator/package.json @@ -32,7 +32,7 @@ "@fern-api/typescript-codegen": "workspace:*", "@fern-api/generator-commons": "workspace:*", "@fern-api/logger": "workspace:*", - "@fern-fern/generator-cli-sdk": "0.0.56", + "@fern-fern/generator-cli-sdk": "0.0.17", "@fern-fern/generator-exec-sdk": "^0.0.898", "@fern-fern/ir-sdk": "53.8.0", "@fern-fern/snippet-sdk": "^0.0.5526", diff --git a/generators/typescript/sdk/generator/src/readme/ReadmeConfigBuilder.ts b/generators/typescript/sdk/generator/src/readme/ReadmeConfigBuilder.ts index d96f8fac260..0d229dcd9c2 100644 --- a/generators/typescript/sdk/generator/src/readme/ReadmeConfigBuilder.ts +++ b/generators/typescript/sdk/generator/src/readme/ReadmeConfigBuilder.ts @@ -45,6 +45,7 @@ export class ReadmeConfigBuilder { organization: context.config.organization, apiReferenceLink: context.ir.readmeConfig?.apiReferenceLink, bannerLink: context.ir.readmeConfig?.bannerLink, + referenceMarkdownPath: "./reference.md", features }; } diff --git a/packages/cli/cli/versions.yml b/packages/cli/cli/versions.yml index f4b4ccb3828..5ee5c3d9f47 100644 --- a/packages/cli/cli/versions.yml +++ b/packages/cli/cli/versions.yml @@ -1,3 +1,29 @@ +- changelogEntry: + - summary: | + Any markdown files that have custom components are also pushed up to the Fern Docs + platform. + type: fix + irVersion: 53 + version: 0.43.8 + +- changelogEntry: + - summary: | + The `valid-markdown` rule has been updated to try and parse the markdown file into a + valid AST. If the file fails to parse, `fern check` will log an error as well + as the path to the markdown. + type: fix + irVersion: 53 + version: 0.43.7 + +- changelogEntry: + - summary: | + The OpenAPI importer now appropriately brings in responses that are under the `text/event-stream` + Content-Type if your endpoint is annotated with `x-fern-streaming`. + If your endpoint is not annotated with `x-fern-streaming`, then the response will be ignored. + type: fix + irVersion: 53 + version: 0.43.6 + - changelogEntry: - summary: | If you use the `x-fern-streaming` extension and want to provide different descriptions diff --git a/packages/cli/docs-importers/commons/.depcheckrc.json b/packages/cli/docs-importers/commons/.depcheckrc.json new file mode 100644 index 00000000000..a3a4f43188c --- /dev/null +++ b/packages/cli/docs-importers/commons/.depcheckrc.json @@ -0,0 +1,10 @@ +{ + "ignores": [ + "@types/jest", + "globals", + "@types/node" + ], + "ignore-patterns": [ + "lib" + ] +} \ No newline at end of file diff --git a/packages/cli/docs-importers/commons/.prettierrc.cjs b/packages/cli/docs-importers/commons/.prettierrc.cjs new file mode 100644 index 00000000000..9b6214d5129 --- /dev/null +++ b/packages/cli/docs-importers/commons/.prettierrc.cjs @@ -0,0 +1 @@ +module.exports = require("../../../../.prettierrc.json"); diff --git a/packages/cli/docs-importers/commons/package.json b/packages/cli/docs-importers/commons/package.json new file mode 100644 index 00000000000..06e5f80555f --- /dev/null +++ b/packages/cli/docs-importers/commons/package.json @@ -0,0 +1,48 @@ +{ + "name": "@fern-api/docs-importer-commons", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "https://github.com/fern-api/fern.git", + "directory": "packages/cli/docs-importers/mintlify" + }, + "private": true, + "files": [ + "lib" + ], + "type": "module", + "source": "src/index.ts", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "sideEffects": false, + "scripts": { + "clean": "rm -rf ./lib && tsc --build --clean", + "compile": "tsc --build", + "test": "vitest --passWithNoTests --run", + "test:update": "vitest --passWithNoTests --run -u", + "lint:eslint": "eslint --max-warnings 0 . --ignore-path=../../../../../.eslintignore", + "lint:eslint:fix": "yarn lint:eslint --fix", + "format": "prettier --write --ignore-unknown --ignore-path ../../../../../shared/.prettierignore \"**\"", + "format:check": "prettier --check --ignore-unknown --ignore-path ../../../../../shared/.prettierignore \"**\"", + "organize-imports": "organize-imports-cli tsconfig.json", + "depcheck": "depcheck" + }, + "dependencies": { + "@fern-api/configuration": "workspace:*", + "@fern-api/fs-utils": "workspace:*", + "@fern-api/task-context": "workspace:*", + "@fern-fern/fdr-cjs-sdk": "0.111.0-51d403bce", + "js-yaml": "^4.1.0" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.8", + "@types/node": "^18.7.18", + "depcheck": "^1.4.6", + "eslint": "^8.56.0", + "globals": "link:@types/vitest/globals", + "organize-imports-cli": "^0.10.0", + "prettier": "^2.7.1", + "typescript": "4.6.4", + "vitest": "^2.0.5" + } +} \ No newline at end of file diff --git a/packages/cli/docs-importers/commons/src/DocsImporter.ts b/packages/cli/docs-importers/commons/src/DocsImporter.ts new file mode 100644 index 00000000000..60a60d71812 --- /dev/null +++ b/packages/cli/docs-importers/commons/src/DocsImporter.ts @@ -0,0 +1,18 @@ +import { FernDocsBuilder } from "./FernDocsBuilder"; +import { TaskContext } from "@fern-api/task-context"; + +export declare namespace DocsImporter { + interface BaseArgs { + context: TaskContext; + } +} + +export abstract class DocsImporter { + protected context: TaskContext; + + constructor({ context }: DocsImporter.BaseArgs) { + this.context = context; + } + + public abstract import({ args, builder }: { args: Args; builder: FernDocsBuilder }): Promise; +} diff --git a/packages/cli/docs-importers/commons/src/FernDocsBuilder.ts b/packages/cli/docs-importers/commons/src/FernDocsBuilder.ts new file mode 100644 index 00000000000..35a3920d024 --- /dev/null +++ b/packages/cli/docs-importers/commons/src/FernDocsBuilder.ts @@ -0,0 +1,62 @@ +import { AbsoluteFilePath, RelativeFilePath } from "@fern-api/fs-utils"; +import { docsYml } from "@fern-api/configuration"; +import { FernRegistry as CjsFdrSdk } from "@fern-fern/fdr-cjs-sdk"; + +/** + * A builder utility to help + */ +export abstract class FernDocsBuilder { + public abstract addOpenAPI({ absolutePathToOpenAPI }: { absolutePathToOpenAPI: AbsoluteFilePath }): void; + + public abstract addMarkdownPage({ + frontmatter, + markdown, + relativeFilePathFromDocsYml + }: { + frontmatter: CjsFdrSdk.docs.latest.Frontmatter; + markdown: string; + relativeFilePathFromDocsYml: RelativeFilePath; + }): void; + + public abstract addAsset({ + absoluteFilePathToAsset, + relativeFilePathFromDocsYml + }: { + absoluteFilePathToAsset: AbsoluteFilePath; + relativeFilePathFromDocsYml: RelativeFilePath; + }): void; + + public abstract addVersion({ + versionConfig, + navigation + }: { + versionConfig: docsYml.RawSchemas.VersionConfig; + navigation: docsYml.RawSchemas.VersionFileConfig; + }): void; + + public abstract getNavigationBuilder({ + tabId, + tabConfig + }?: { + tabId: docsYml.RawSchemas.TabId; + tabConfig: docsYml.RawSchemas.TabConfig; + }): FernDocsNavigationBuilder; + + public abstract addNavbarLink({ link }: { link: docsYml.RawSchemas.NavbarLink }): void; + + public abstract setTitle({ title }: { title: string }): void; + + public abstract setFavicon({ favicon }: { favicon: RelativeFilePath }): void; + + public abstract setLogo({ logo }: { logo: docsYml.RawSchemas.LogoConfiguration }): void; + + public abstract setColors({ colors }: { colors: docsYml.RawSchemas.ColorsConfiguration }): void; + + public abstract setLayout({ layout }: { layout: docsYml.RawSchemas.LayoutConfig }): void; + + public abstract build({ outputDirectory }: { outputDirectory: AbsoluteFilePath }): void; +} + +export abstract class FernDocsNavigationBuilder { + public abstract addItem({ item }: { item: docsYml.RawSchemas.NavigationItem }): void; +} diff --git a/packages/cli/docs-importers/commons/src/FernDocsBuilderImpl.ts b/packages/cli/docs-importers/commons/src/FernDocsBuilderImpl.ts new file mode 100644 index 00000000000..477e6b62ac4 --- /dev/null +++ b/packages/cli/docs-importers/commons/src/FernDocsBuilderImpl.ts @@ -0,0 +1,175 @@ +import { docsYml, DOCS_CONFIGURATION_FILENAME, FERN_DIRECTORY } from "@fern-api/configuration"; +import { AbsoluteFilePath, dirname, join, RelativeFilePath } from "@fern-api/fs-utils"; +import { FernDocsBuilder, FernDocsNavigationBuilder } from "./FernDocsBuilder"; +import { mkdir, writeFile, cp } from "fs/promises"; +import yaml from "js-yaml"; +import { FernRegistry as CjsFdrSdk } from "@fern-fern/fdr-cjs-sdk"; + +interface MarkdownPage { + frontmatter: CjsFdrSdk.docs.latest.Frontmatter; + markdown: string; +} + +interface Asset { + absoluteFilePathToAsset: AbsoluteFilePath; +} + +export class FernDocsBuilderImpl extends FernDocsBuilder { + private absolutePathsToOpenAPI: AbsoluteFilePath[] = []; + private nonTabbedNavigation: NonTabbedNavigationBuilderImpl = new NonTabbedNavigationBuilderImpl(); + private tabbedNavigation: Record = {}; + private markdownPages: Record = {}; + private assets: Record = {}; + private docsYml: docsYml.RawSchemas.DocsConfiguration = { + instances: [] + }; + + public addOpenAPI({ absolutePathToOpenAPI }: { absolutePathToOpenAPI: AbsoluteFilePath }): void { + this.absolutePathsToOpenAPI.push(absolutePathToOpenAPI); + } + + public addMarkdownPage({ + frontmatter, + markdown, + relativeFilePathFromDocsYml + }: { + frontmatter: CjsFdrSdk.docs.latest.Frontmatter; + markdown: string; + relativeFilePathFromDocsYml: RelativeFilePath; + }): void { + this.markdownPages[relativeFilePathFromDocsYml] = { markdown, frontmatter }; + } + + public addAsset({ + absoluteFilePathToAsset, + relativeFilePathFromDocsYml + }: { + absoluteFilePathToAsset: AbsoluteFilePath; + relativeFilePathFromDocsYml: RelativeFilePath; + }): void { + this.assets[relativeFilePathFromDocsYml] = { absoluteFilePathToAsset }; + } + + public getNavigationBuilder(args?: { + tabId: string; + tabConfig: docsYml.RawSchemas.TabConfig; + }): FernDocsNavigationBuilder { + if (args != null) { + let navigationBuilder = this.tabbedNavigation[args.tabId]; + if (navigationBuilder == null) { + navigationBuilder = new TabbedNavigationBuilderImpl(args.tabId, args.tabConfig); + this.tabbedNavigation[args.tabId] = navigationBuilder; + } + return navigationBuilder; + } + return this.nonTabbedNavigation; + } + + public addVersion({ + versionConfig, + navigation + }: { + versionConfig: docsYml.RawSchemas.VersionConfig; + navigation: docsYml.RawSchemas.VersionFileConfig; + }): void { + throw new Error("Method not implemented."); + } + + public addNavbarLink({ link }: { link: docsYml.RawSchemas.NavbarLink }): void { + if (this.docsYml.navbarLinks == null) { + this.docsYml.navbarLinks = [link]; + } + this.docsYml.navbarLinks.push(link); + } + + public setTitle({ title }: { title: string }): void { + this.docsYml.title = title; + } + + public setFavicon({ favicon }: { favicon: RelativeFilePath }): void { + this.docsYml.favicon = favicon; + } + + public setLogo({ logo }: { logo: docsYml.RawSchemas.LogoConfiguration }): void { + this.docsYml.logo = logo; + } + + public setColors({ colors }: { colors: docsYml.RawSchemas.ColorsConfiguration }): void { + this.docsYml.colors = colors; + } + + public setLayout({ layout }: { layout: docsYml.RawSchemas.LayoutConfig }): void { + this.docsYml.layout = layout; + } + + public async build({ outputDirectory }: { outputDirectory: AbsoluteFilePath }): Promise { + const absolutePathToFernDirectory = join(outputDirectory, RelativeFilePath.of(FERN_DIRECTORY)); + await mkdir(absolutePathToFernDirectory, { recursive: true }); + + if (Object.keys(this.tabbedNavigation).length > 0) { + this.docsYml.tabs = Object.fromEntries( + Object.entries(this.tabbedNavigation).map(([key, value]) => { + return [value.tabId, value.tabConfig]; + }) + ); + const tabbedNavigationItems: docsYml.RawSchemas.TabbedNavigationItem[] = []; + Object.entries(this.tabbedNavigation).forEach(([_, value]) => { + const tabbedItem: docsYml.RawSchemas.TabbedNavigationItem = { + tab: value.tabId, + layout: value.items + }; + tabbedNavigationItems.push(tabbedItem); + }); + this.docsYml.navigation = tabbedNavigationItems; + } else if (this.nonTabbedNavigation != null) { + this.docsYml.navigation = this.nonTabbedNavigation.items; + } + + const absoluteFilePathToDocsYml = join( + absolutePathToFernDirectory, + RelativeFilePath.of(DOCS_CONFIGURATION_FILENAME) + ); + await writeFile(absoluteFilePathToDocsYml, yaml.dump(this.docsYml)); + + await Promise.all( + Object.entries(this.markdownPages).map(async ([filepath, page]) => { + const absoluteFilepathToMarkdownPage = join(absolutePathToFernDirectory, RelativeFilePath.of(filepath)); + await mkdir(dirname(absoluteFilepathToMarkdownPage), { recursive: true }); + const frontmatter = + Object.keys(page.frontmatter).length > 0 + ? `---\n${yaml.dump(JSON.parse(JSON.stringify(page.frontmatter)))}---\n\n` + : ""; + await writeFile(absoluteFilepathToMarkdownPage, `${frontmatter}${page.markdown}`); + }) + ); + + await Promise.all( + Object.entries(this.assets).map(async ([filepath, asset]) => { + const absolutePathToAsset = join(absolutePathToFernDirectory, RelativeFilePath.of(filepath)); + await mkdir(dirname(absolutePathToAsset), { recursive: true }); + await cp(asset.absoluteFilePathToAsset, absolutePathToAsset); + }) + ); + } +} + +export class TabbedNavigationBuilderImpl implements FernDocsNavigationBuilder { + public items: docsYml.RawSchemas.NavigationItem[] = []; + + public constructor( + public readonly tabId: docsYml.RawSchemas.TabId, + public readonly tabConfig: docsYml.RawSchemas.TabConfig + ) {} + + public addItem({ item }: { item: docsYml.RawSchemas.NavigationItem }): void { + this.items.push(item); + } +} + +export class NonTabbedNavigationBuilderImpl implements FernDocsNavigationBuilder { + public items: docsYml.RawSchemas.NavigationItem[] = []; + + public addItem({ item }: { item: docsYml.RawSchemas.NavigationItem }): void { + this.items.push(item); + } +} diff --git a/packages/cli/docs-importers/commons/src/index.ts b/packages/cli/docs-importers/commons/src/index.ts new file mode 100644 index 00000000000..6d911a1a755 --- /dev/null +++ b/packages/cli/docs-importers/commons/src/index.ts @@ -0,0 +1,3 @@ +export { DocsImporter } from "./DocsImporter"; +export { FernDocsBuilder, FernDocsNavigationBuilder } from "./FernDocsBuilder"; +export { FernDocsBuilderImpl } from "./FernDocsBuilderImpl"; diff --git a/packages/cli/docs-importers/commons/tsconfig.json b/packages/cli/docs-importers/commons/tsconfig.json new file mode 100644 index 00000000000..dd3f3d0394a --- /dev/null +++ b/packages/cli/docs-importers/commons/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../../shared/tsconfig.shared.json", + "compilerOptions": { "composite": true, "outDir": "lib", "rootDir": "src" }, + "include": ["./src/**/*"], + "references": [ { "path": "../../../commons/core-utils" }, { "path": "../../../commons/fs-utils" }, { "path": "../../task-context" } ] +} diff --git a/packages/cli/docs-importers/commons/vitest.config.ts b/packages/cli/docs-importers/commons/vitest.config.ts new file mode 100644 index 00000000000..d11017dc676 --- /dev/null +++ b/packages/cli/docs-importers/commons/vitest.config.ts @@ -0,0 +1 @@ +export { default } from "../../../../shared/vitest.config"; diff --git a/packages/cli/docs-importers/mintlify/.depcheckrc.json b/packages/cli/docs-importers/mintlify/.depcheckrc.json new file mode 100644 index 00000000000..a3a4f43188c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/.depcheckrc.json @@ -0,0 +1,10 @@ +{ + "ignores": [ + "@types/jest", + "globals", + "@types/node" + ], + "ignore-patterns": [ + "lib" + ] +} \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/.prettierrc.cjs b/packages/cli/docs-importers/mintlify/.prettierrc.cjs new file mode 100644 index 00000000000..9b6214d5129 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/.prettierrc.cjs @@ -0,0 +1 @@ +module.exports = require("../../../../.prettierrc.json"); diff --git a/packages/cli/docs-importers/mintlify/package.json b/packages/cli/docs-importers/mintlify/package.json new file mode 100644 index 00000000000..acc9dcfc8ad --- /dev/null +++ b/packages/cli/docs-importers/mintlify/package.json @@ -0,0 +1,50 @@ +{ + "name": "@fern-api/mintlify-importer", + "version": "0.0.0", + "repository": { + "type": "git", + "url": "https://github.com/fern-api/fern.git", + "directory": "packages/cli/docs-importers/mintlify" + }, + "private": true, + "files": [ + "lib" + ], + "type": "module", + "source": "src/index.ts", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "sideEffects": false, + "scripts": { + "clean": "rm -rf ./lib && tsc --build --clean", + "compile": "tsc --build", + "test": "vitest --run", + "test:update": "vitest --run -u", + "lint:eslint": "eslint --max-warnings 0 . --ignore-path=../../../../../.eslintignore", + "lint:eslint:fix": "yarn lint:eslint --fix", + "format": "prettier --write --ignore-unknown --ignore-path ../../../../../shared/.prettierignore \"**\"", + "format:check": "prettier --check --ignore-unknown --ignore-path ../../../../../shared/.prettierignore \"**\"", + "organize-imports": "organize-imports-cli tsconfig.json", + "depcheck": "depcheck" + }, + "dependencies": { + "@fern-api/configuration": "workspace:*", + "@fern-api/docs-importer-commons": "workspace:*", + "@fern-api/core-utils": "workspace:*", + "@fern-api/task-context": "workspace:*", + "@fern-api/fs-utils": "workspace:*", + "@fern-api/logger": "workspace:*", + "@fern-fern/fdr-cjs-sdk": "0.111.0-51d403bce", + "gray-matter": "^4.0.3" + }, + "devDependencies": { + "@types/node": "^18.7.18", + "depcheck": "^1.4.6", + "eslint": "^8.56.0", + "globals": "link:@types/vitest/globals", + "organize-imports-cli": "^0.10.0", + "prettier": "^2.7.1", + "typescript": "4.6.4", + "vitest": "^2.0.5" + } +} \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/MintlifyImporter.ts b/packages/cli/docs-importers/mintlify/src/MintlifyImporter.ts new file mode 100644 index 00000000000..4aeb6008e14 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/MintlifyImporter.ts @@ -0,0 +1,131 @@ +import { AbsoluteFilePath, dirname, join, RelativeFilePath } from "@fern-api/fs-utils"; +import { DocsImporter, FernDocsNavigationBuilder } from "@fern-api/docs-importer-commons"; +import { FernDocsBuilder } from "@fern-api/docs-importer-commons"; +import { readFile } from "fs/promises"; +import { MintJsonSchema, MintNavigationItem } from "./mintlify"; +import { convertLogo } from "./convertLogo"; +import { convertColors } from "./convertColors"; +import { convertNavigationItem } from "./convertNavigationItem"; +import { getTabForMintItem } from "./utils/getTabForMintItem"; +import { stripLeadingSlash } from "@fern-api/core-utils"; + +export declare namespace MintlifyImporter { + interface Args { + absolutePathToMintJson: AbsoluteFilePath; + } +} + +interface TabInfo { + name: string; + url: string; + navigationBuilder: FernDocsNavigationBuilder; +} + +export class MintlifyImporter extends DocsImporter { + private documentationTab: TabInfo | undefined = undefined; + private tabUrlToInfo: Record = {}; + + public async import({ args, builder }: { args: MintlifyImporter.Args; builder: FernDocsBuilder }): Promise { + const mintJsonContent = await readFile(args.absolutePathToMintJson, "utf-8"); + const mint = JSON.parse(mintJsonContent) as MintJsonSchema; + + builder.setTitle({ title: mint.name }); + + const relativePathToFavicon = RelativeFilePath.of(mint.favicon.substring(1)); + builder.setFavicon({ favicon: relativePathToFavicon }); + builder.addAsset({ + absoluteFilePathToAsset: join(dirname(args.absolutePathToMintJson), relativePathToFavicon), + relativeFilePathFromDocsYml: relativePathToFavicon + }); + + const logo = convertLogo({ logo: mint.logo, builder, absolutePathToMintJson: args.absolutePathToMintJson }); + if (logo != null) { + builder.setLogo({ logo }); + } + this.context.logger.debug("Converted logo"); + + const colors = convertColors(mint.colors); + if (colors != null) { + builder.setColors({ colors }); + } + this.context.logger.debug("Converted color configuration"); + + for (const tab of mint.tabs ?? []) { + this.tabUrlToInfo[tab.url] = { + name: tab.name, + url: tab.url, + navigationBuilder: builder.getNavigationBuilder({ + tabId: tab.url, + tabConfig: { slug: tab.url, displayName: tab.name } + }) + }; + } + + for (const mintItem of mint.navigation) { + const section = await convertNavigationItem({ + absolutePathToMintJson: args.absolutePathToMintJson, + item: mintItem, + builder, + context: this.context + }); + const nav = await this.getNavigationBuilder({ mintItem, builder }); + if (section != null) { + nav.addItem({ item: section }); + } + } + this.context.logger.debug("Converted navigation"); + + if (typeof mint.openapi === "string") { + const absolutePathToOpenAPI = join( + dirname(args.absolutePathToMintJson), + RelativeFilePath.of(stripLeadingSlash(mint.openapi)) + ); + builder.addOpenAPI({ + absolutePathToOpenAPI + }); + } else if (mint.openapi != null) { + for (const openapi of mint.openapi) { + const absolutePathToOpenAPI = join( + dirname(args.absolutePathToMintJson), + RelativeFilePath.of(stripLeadingSlash(openapi)) + ); + builder.addOpenAPI({ + absolutePathToOpenAPI + }); + } + } + this.context.logger.debug("Imported OpenAPI specs"); + } + + private async getNavigationBuilder({ + mintItem, + builder + }: { + mintItem: MintNavigationItem; + builder: FernDocsBuilder; + }): Promise { + if (Object.keys(this.tabUrlToInfo).length > 0) { + const tabUrl = getTabForMintItem({ mintItem }); + if (tabUrl == null) { + return this.context.failAndThrow(`Failed to assign navigation item to a tab group: ${mintItem.group}`); + } + const tab = this.tabUrlToInfo[tabUrl] ?? this.getDefaultDocumentationTab(builder); + return tab.navigationBuilder; + } + return builder.getNavigationBuilder(); + } + + private getDefaultDocumentationTab(builder: FernDocsBuilder): TabInfo { + if (this.documentationTab == null) { + this.documentationTab = { + name: "Documentation", + url: "documentation", + navigationBuilder: builder.getNavigationBuilder({ + tabId: "documentation", + tabConfig: { displayName: "Documentation", slug: "documentation" } + }) + }; + } + return this.documentationTab; + } +} diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/README.md b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/README.md new file mode 100644 index 00000000000..6076636c92b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/README.md @@ -0,0 +1,32 @@ +# Mintlify Starter Kit + +Click on `Use this template` to copy the Mintlify starter kit. The starter kit contains examples including + +- Guide pages +- Navigation +- Customizations +- API Reference pages +- Use of popular components + +### 👩‍💻 Development + +Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) to preview the documentation changes locally. To install, use the following command + +``` +npm i -g mintlify +``` + +Run the following command at the root of your documentation (where mint.json is) + +``` +mintlify dev +``` + +### 😎 Publishing Changes + +Install our Github App to autopropagate changes from youre repo to your deployment. Changes will be deployed to production automatically after pushing to the default branch. Find the link to install on your dashboard. + +#### Troubleshooting + +- Mintlify dev isn't running - Run `mintlify install` it'll re-install dependencies. +- Page loads as a 404 - Make sure you are running in a folder with `mint.json` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/_snippets/snippet-example.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/_snippets/snippet-example.mdx new file mode 100644 index 00000000000..089334c54f9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/_snippets/snippet-example.mdx @@ -0,0 +1,3 @@ +## My Snippet + +This is an example of a reusable snippet diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch.mdx new file mode 100644 index 00000000000..404611482a5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch.mdx @@ -0,0 +1,93 @@ +--- +title: "Batch Calling" +api: "POST https://api.bland.ai/batch" +description: "Send a series of calls with a single api call" +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + This is the prompt or task used for all the phone calls in the request. Information can be inserted into it surrounding variable names with \{\{curly braces\}\}. + +Example: + +```json +"You are calling {{business}} to renew their subscription to {{service}} before it expires on {{date}}." +``` + + + + + Define a list of calls to make and their properties. + + Each call in call_data *must* have a `phone_number` property. Properties are case-sensitive. + +Example: + +```json +[ + { + "phone_number": "1234567890", + "business": "ABC co.", + "service": "Netflix", + "date": "September 4th" + }, + { + "phone_number": "32176540987", + "business": "XYZ inc.", + "service": "Window Cleaning", + "date": "December 20th" + } +] +``` + + + + + Adds a user-friendly label to your batch to keep track of it's original intention. This can help differentiate + multiple call batches that are part of the same Campaign. Shown when a batch is retreived. + + + + Use ```campaign_id``` to organize related batches together. This can be set manually or auto-generated through + Campaigns. + + + + When this is set to ```true```, only the first call of ```call_data``` will be dispatched. A common use case is to set the first ```phone_number``` value to your own to confirm everything's set up properly. + +Includes additional information in the response when true so that it's easier to find any issues. + + + + + All other parameters supported by the [Send Call](/api-v1/post/calls) endpoint are supported here as well. They will + be applied to each call in the batch. + + +### Response + + + If anything other than "success" is returned, there was an error. + + + + The unique identifier for the batch. + + + + +```json Response +{ + "message": "success", + "batch_id": "3p$7rQ3p9sT5bzmF-gen-batch" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch_get.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch_get.mdx new file mode 100644 index 00000000000..84f9ea94257 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch_get.mdx @@ -0,0 +1,266 @@ +--- +title: "Retrieve a Batch" +api: "GET https://api.bland.ai/batch" +description: "Retrieves calls and batch data for a specific batch_id." +--- + +### Headers + + + Your API key for authentication. + + +### Query Parameters + + + The unique identifier for the batch of calls you want to retrieve. + + + + Whether or not to include individual call data in the response. + + + + If calls are included, can be set to false to exclude transcripts from the response. + + + + If calls are included, can be set to false to exclude analysis from the response. + + +### Response + + + An object containing parameters and settings for the batch. + + + + The unique identifier of the batch - used as the `batch_id` parameter in other API calls. + + + + The creation timestamp of the batch. + + + + The label or description of the batch. + + + + The base prompt used for calls in this batch. + + + + The endpoint code used for API integration. + + + + An object containing parameters for the calls in the batch. + + + + An object containing analysis data for the batch. + + + + The total number of calls in the batch, including completed and in-progress calls. + + + + The total number of completed calls in the batch. + + + + The total number of in-progress calls in the batch. + + + + An object containing the number of calls in each queue status. + +Example: + +```json +{ + "complete": 237, + "queued": 2, + "call_error": 1 +} +``` + + + + + Contains `average`, `average_nonzero`, `summary` and `all` fields. + +- `average`: The average call length in seconds. +- `average_nonzero`: The average call length in seconds, excluding calls with a length of less than a second. +- `summary`: A summary of the call lengths, grouped into ranges. +- `all`: Contains each call length, in case you want to use a different grouping than the default. + + + + Contains each `call_id` in the batch. + + + + Contains any error messages that calls in the batch may have. + +Example: + +```json +[ + { + "call_id": "c52f5f8c-147e-478c-4b40-88214feeba29", + "error_message": "Cannot transfer to +12223334444 - Call is no longer active" + } +] +``` + + + + + Contains the number of calls that have been sent to each endpoint. Applicable only to API integrations. + + + + An array of objects, each representing individual call data. + + + + The timestamp when the individual call was created. + + + + The phone number the call was made to. + + + + The phone number the call was made from. + + + + Indicates if the call was completed. + + + + The unique identifier for each individual call. + + + + The duration of the call in minutes. + + + + +```json Response +{ + "batch_params": { + "id": "AAcQq8zXxLB56LWg-gen-batch", + "campaign_id": null, + "created_at": "2023-12-07T20:43:44.544773+00:00", + "label": "Customer Satisfaction Follow-up", + "base_prompt": "You are calling {{name}} about their recent purchase of the item: {{item}}. Ask them each of the following questions about the specific item they purchased: {{questions}}", + "endpoint_code": "api", + "call_params": { + "reduce_latency": true, + "voice_id": 0, + "language": "eng", + "max_duration": 10, + "wait_for_greeting": false + } + }, + "analysis": { + "total_calls": 88, + "completed_calls": 86, + "in_progress_calls": 2, + "queue_statuses": { + "complete": 85, + "started": 2, + "call_error": 1 + }, + "call_lengths": { + "average": 17, + "average_nonzero": 31, + "summary": { + "0-5": 18, + "5-10": 4, + "10-15": 3, + "15-20": 2, + "20-30": 14, + "30-45": 28, + "45-60": 11, + "60-90": 6, + "90-120": 1, + "120+": 1 + }, + "all": [ + 7, 32 + //... + ] + }, + "call_ids": [ + "ffa99be3-63dd-44dc-9523-380cd25c1b9e", + "591338a8-34b2-41e6-93da-b9029c9bdedc" + //... + ], + "error_messages": [ + { + "call_id": "c52f5f8c-147e-478c-4b40-88214feeba29", + "error_message": "Cannot transfer to +12223334444 - Call is no longer active" + } + ], + "endpoints": { + "api.bland.ai": 88 + } + }, + "call_data": [ + { + "status": "completed", + "corrected_duration": "12", + "end_at": "2023-12-16T00:17:38.000Z", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e", + "to": "1112223333", + "from": "+17473423273", + "completed": true, + "created_at": "2023-12-16T00:17:22.383682+00:00", + "queue_status": "complete", + "endpoint_url": "api.bland.ai", + "max_duration": 30, + "error_message": null, + "request_data": { + "phone_number": "1112223333", + "reduce_latency": true, + "wait": false, + "language": "ENG" + }, + "transcripts": [ + { + "id": 1188954, + "created_at": "2023-12-16T00:17:30.46833+00:00", + "text": " Hi, Im calling about the laundromat for sale. —  ", + "user": "assistant", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + }, + { + "id": 1188957, + "created_at": "2023-12-16T00:17:35.14056+00:00", + "text": "I'll get back to you as soon as you can. Just leave a message. Thank you. Bye.", + "user": "user", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + }, + { + "id": 1188959, + "created_at": "2023-12-16T00:17:36.710551+00:00", + "text": "Ended call: Goodbye", + "user": "agent-action", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + } + ], + "call_length": 6.242 + } + //... + ] +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch_stop.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch_stop.mdx new file mode 100644 index 00000000000..4b045b79382 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batch_stop.mdx @@ -0,0 +1,49 @@ +--- +title: "Cancel Batch" +api: "POST https://api.bland.ai/batch/stop" +description: "Terminates every dispatched call for a specific batch." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + The unique identifier for the batch to be cancelled. + + +### Response + + + The status of the request. If anything other than 'success', an error has occurred or all calls have already been + completed. + + + + A message describing the status of the request. + + + + The number of calls that were cancelled. + + + + The `batch_id` of the cancelled batch. + + + + +```json Response +{ + "status": "success", + "message": "Successfully stopped batch", + "num_calls": 1204, + "batch_id": "5e5b1a3a-2b0a-4b9e-8b9e-asdf-gen-batch" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batches_get.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batches_get.mdx new file mode 100644 index 00000000000..9201726c1e8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/batches_get.mdx @@ -0,0 +1,77 @@ +--- +title: "Retrieve All Batches" +api: "GET https://api.bland.ai/batches" +description: "Retrieves batch-specific data for each batch you've created." +--- + +### Headers + + + Your API key for authentication. + + +### Response + + + Can be `success` or `error`. + + + + Contains an array of batch objects. + + + + The unique identifier for the batch. + + + + The original base prompt used to create the batch. Will still contain the original placeholder variables such as ` + {{ business }}` or `{{ name }}`. + + + + The label you assigned to the batch (if any). + + + + Enterprise customers with custom endpoints will see the endpoint code here if specified. + + + + The base call parameters used to create the batch, such as `voice_id`, `max_duration`, `reduce_latency`, and + `wait_for_greeting`. + + + + The date and time the batch was created. + + + +```json Response +{ + "status": "success", + "batches": [ + { + "batch_id": "ZfowpkhOSVCZJ94i-gen-batch", + "campaign_id": "a2shduf92f74p8288c93nid5", + "created_at": "2023-11-16T22:14:24.9663+00:00", + "label": "Subscription Renewal Reminders", + "base_prompt": "You are calling {{business}} and need to let them know that their subscription to {{service}} is going to expire on {{date}}. If they'd like to renew, take their credit card information and bill them through {{url}}", + "endpoint_code": "api", + "call_params": { + "reduce_latency": true, + "voice_id": 2, + "language": "eng", + "request_data": { + "test_param": "request data.test_param", + "your name": "Janessa" + }, + "max_duration": 5, + "wait_for_greeting": false + } + }, + //... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/end_batches.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/end_batches.mdx new file mode 100644 index 00000000000..1240bd2f4c5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/batch-endpoint/end_batches.mdx @@ -0,0 +1,47 @@ +--- +title: "Cancel All Batch Calls" +api: "POST https://api.bland.ai/end_batches" +description: "Terminates every dispatched call in each running batch." +--- + +### Headers + + + Your API key for authentication. + + +### Response + + + The status of the request. If anything other than 'success', an error has occurred or all calls have already been + completed. + + + + A message describing the status of the request. + + + + The number of calls that were cancelled. + + + + An array of batch_ids that were cancelled. + + + + +```json Response +{ + "status": "success", + "message": "Successfully stopped batch", + "num_calls": 9704, + "batch_ids": [ + "5e5b1a3a-2b0a-4b9e-8b9e-e42a-gen-batch", + "b4eb1a3a-4b9e-230a-8b9e-acdc-gen-batch" + "92ab1a3a-a82e-339b-4b9e-8b9e-gen-batch" + ] +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/call.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/call.mdx new file mode 100644 index 00000000000..857dbc618c5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/call.mdx @@ -0,0 +1,270 @@ +--- +title: "Make a call" +api: "POST https://api.bland.ai/call" +description: "This endpoint sends an outbound AI phone call." +--- + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to call. Country code required for non-US numbers. + +Example: `+12223334444`, `+44770090000`, `+61491550156`. + + + + + A phone number that the agent can transfer to under specific conditions, such as when the caller/callee asks to speak to a human. + +For best results, specify the exact conditions to transfer under in the `task` parameter. Refer to the action as "transfer", any other phrasing such as "swap" or "switch" can cause unexpected behavior. + +Example: `+12223334444`, `+44770090000`, `+61491550156`. + + + + + Specify a purchased Outbound Number to call from. Country code is required, spaces or parentheses must be excluded. + +By default, calls are initiated from a separate pool of numbers owned by Bland. + + + + + Define how the AI should behave. Provide instructions, relevant information, and examples of the ideal conversation flow. + +Note: Concise prompts lead to higher performance and adherence to instructions. Overly verbose prompts 'dilute' the context if filled with unnecessary/irrelevant details. + + + + + Reducing latency means that the agent will generate responses more quickly and have less of a delay. This must be set + to ```true``` when using Voice Clones. + + + + When reduce latency is set to `true` (default): + +- 0: American male +- 1: Australian female +- 2: American female + +When reduce latency is set to `false`: + +- 0: American female (southern accent) +- 1: American male +- 2: British female +- 3: Indian male + + + + The webhook should be a http / https callback url. We will send the call_id and transcript to this URL after the call + completes. This can be useful if you want to have real time notifications when calls finish. + + + + Should the AI speak first or wait for someone else to talk? + + Creates more realistic conversations when answered with "Hello?", "This is \{name\} speaking." and so on. + + - When ```false```: The AI starts speaking shortly after the call is answered. + + - When ```true```: The AI will wait for the answerer to speak. + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without + `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + + + + To record your phone call, set `record` to true. When your call completes, you can access the recording by requesting + the `/call/recording` endpoint. + + + + + + Note: This is an experimental parameter and may behave unexpectedly. + +Adjust the predictability and consistency of the AI agent's voice. Lower values allow larger deviations from the baseline voice, whether default or cloned. Setting this too high however can cause a monotone voice. + +Accepts decimal values between `0` and `1` (inclusive). + + + + Note: This is an experimental parameter and may behave unexpectedly. + +Higher values will make speech differences between the selected voice and others more prominent. Extremely high values can cause voice distortion. + +Use lower values to lower the distinctiveness of the voice or eliminate unwanted audio static spikes. + +Accepts decimal values between `0` and `1` (inclusive). + + + + + Note: This is an experimental parameter and may behave unexpectedly. + + Note #2: Setting `reduce_latency` to `false` will cause this parameter to be ignored. + +How fast your agent talks! This parameter is simply a speech-speed multiplier, and works with fractional values such as `0.5` or large ones like `2`. + +Accepts decimal values between `0.1` and `5` (inclusive). + + + + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```eng``` - Spanish: ```esp``` - French: + ```fre``` - Polish: ```pol``` + + + + Set the longest you want the call to possibly go in minutes. After the max_duration minutes have passed, the call will automatically end. + + Example Values: ```"45", "5.5", 20, 2.8``` + + + + [ + { + "word": "example", + "pronunciation": "ex-am-ple", + "case_sensitive": "false", + "spaced": "false" + }, + { + "word": "API", + "pronunciation": "A P I", + "case_sensitive": "true", + "spaced": "true" + } + ] + + + + + + + + A value between 0 and 1 that controls the randomness of the LLM. 0 will cause more deterministic outputs while 1 will cause more random. + + Example Values: ```"0.9", "0.3", "0.5"``` + + + + AMD mode helps our AI navigate phone trees and IVR systems. If you know your call will hit an automated system you + should switch it on. NOTE: AMD mode causes increased delay for the first response, even if answered by a human. Highly + recommended to set to `false` in the majority of cases. + + + + When you want your AI to "know" a specific fact - like the caller's + name or other relevant context. + +The AI agent will be aware of both the key names as well as their corresponding values. + + + + + + A set of external API requests to fetch at the start of the call or repeatedly. + +Each request object should contain: + +`url`: The URL of the external API to fetch data from. + +`response_data`: An array of objects describing how to parse and use the data fetched from the API. Explained in more detail below. + +The following are optional: + +`method`: Allows `GET` or `POST`. Default: `GET` + +`cache`: Whether to fetch the data once at the beginning of the call, or to re-check continuously for data that might change mid-call. Default: `true` + +`headers`: An object of headers to send with the request. + +`body`: The body of the request. + +The following variables can be injected into the dynamic request body: + +- `{{from}}` (Ex. `+12223334444`) +- `{{to}}` +- `{{short_from}}` (Ex. `2223334444`) +- `{{short_to}}` +- `{{call_id}}` + +These string values will be replaced in each `dynamic_data[].body` where they're used by system values in each request. + +Try out with this example: + +```json + "dynamic_data": [ + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json", + "response_data": [ + { + "name": "BTC Price USD", + "data": "bpi.USD.rate", + "context": "Current BTC Price: ${{BTC Price USD}} USD" + }, + { + "name": "BTC Price EUR", + "data": "bpi.EUR.rate", + "context": "In Euros: {{BTC Price USD}} EUR" + } + ] + } + ] +``` + + +An array of objects describing how to parse and use the data fetched from the API. + +Each object in this array should contain: + +- `name`: A label for the fetched data. +- Example: `"Flight Status"` +- `data`: The JSON path in the API response to extract the data from. +- Example: `"user.flights[0].status"` +- `context`: How this data should be incorporated into the AI's knowledge. +- Example: `"John's flight is currently {{Flight Status}}"` + + + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `500` milliseconds. You'll encounter higher latency, but you'll be interrupted much less frequently. + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/dynamic_validate.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/dynamic_validate.mdx new file mode 100644 index 00000000000..8d3878c2588 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/dynamic_validate.mdx @@ -0,0 +1,151 @@ +--- +title: "Validate Dynamic Data" +api: "POST https://api.bland.ai/dynamic_data/test" +description: "This endpoint is designed for validating and processing dynamic data obtained from external APIs. The data is used in AI phone calls to ensure compliance with format requirements and functionality." +--- + +### Headers + + + Your unique API key for authentication. This key must be included in the header of each request. + + +### Body + + + An array of objects, each specifying an external API to fetch data from. These objects must include the API endpoint, + the HTTP method, a cache flag, and details on parsing and integrating the response. + + +### Response + + + Indicates the outcome of the validation process. Possible values are `success` or `error`. + + + + The duration in milliseconds from receiving the request to completing the data fetch. + + + + Contains the results from the dynamic data fetch. Each object in this array shows the cache status, the name of the + data, and the processed prompt with the inserted data. + + + + Reflects the original dynamic data request for cross-reference purposes. + + + + The parsed data from fetching the dynamic data. This can be used in other requests, the `task` or `prompt` fields, + even other dynamic data requests through the `{{ variable }}` syntax. + + + + Contains the raw responses from the external APIs. This is useful for debugging purposes. + + + +```json +{ + "status": "success", + "elapsed": 342, + "dynamic_data_response": [ + { + "cached": false, + "name": "Cat Fact", + "prompt": "\n\n Fun fact about cats: A cat can travel at a top speed of approximately 31 mph (49 km) over a short distance." + }, + { + "cached": true, + "name": "BTC Price USD", + "prompt": "\n\n Current Bitcoin value: $42,489.3598 USD" + }, + { + "cached": true, + "name": "BTC Price EUR", + "prompt": "\n\n Current Bitcoin value: €41,390.8399 EUR" + } + ], + "dynamic_data_request": [ + { + "url": "https://catfact.ninja/fact", + "method": "GET", + "cache": false, + "response_data": [ + { + "name": "Cat Fact", + "data": "fact", + "context": "Fun fact about cats: {{Cat Fact}}" + } + ] + }, + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json", + "cache": true, + "response_data": [ + { + "name": "BTC Price USD", + "data": "bpi.USD.rate", + "context": "Current Bitcoin value: ${{BTC Price USD}} USD" + }, + { + "name": "BTC Price EUR", + "data": "bpi.EUR.rate", + "context": "Current Bitcoin value: €{{BTC Price EUR}} EUR" + } + ] + } + ], + "variables": { + "Cat Fact": "A cat can travel at a top speed of approximately 31 mph (49 km) over a short distance.", + "BTC Price USD": "42,489.3598", + "BTC Price EUR": "41,390.8399" + }, + "url_responses": [ + { + "url": "https://catfact.ninja/fact", + "body": { + "fact": "A cat can travel at a top speed of approximately 31 mph (49 km) over a short distance.", + "length": 86 + } + }, + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json", + "body": { + "time": { + "updated": "Dec 31, 2023 14:52:00 UTC", + "updatedISO": "2023-12-31T14:52:00+00:00", + "updateduk": "Dec 31, 2023 at 14:52 GMT" + }, + "disclaimer": "This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org", + "chartName": "Bitcoin", + "bpi": { + "USD": { + "code": "USD", + "symbol": "$", + "rate": "42,489.3598", + "description": "United States Dollar", + "rate_float": 42489.3598 + }, + "GBP": { + "code": "GBP", + "symbol": "£", + "rate": "35,503.7691", + "description": "British Pound Sterling", + "rate_float": 35503.7691 + }, + "EUR": { + "code": "EUR", + "symbol": "€", + "rate": "41,390.8399", + "description": "Euro", + "rate_float": 41390.8399 + } + } + } + } + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/end.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/end.mdx new file mode 100644 index 00000000000..057baac9075 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/end.mdx @@ -0,0 +1,38 @@ +--- +title: "End phone call" +api: "POST https://api.bland.ai/end" +description: "Send a POST request to end a phone call that's in progress" +--- + +### Headers + + + Your API key for authentication. + +### Body + + + The unique identifier for the call. + + +### Response + + + Can be `success` or `error`. + + + + If the status is `success`, the message will say "Call ended successfully." Otherwise if the status is `error`, the + message will say "SID not found for the given c_id." or "Internal server error." + + + + +```json Response +{ + "status": "success", + "message": "Call ended successfully." +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/hold.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/hold.mdx new file mode 100644 index 00000000000..a1e623b33a1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/hold.mdx @@ -0,0 +1,50 @@ +--- +title: "Wait on Hold (HoldForMe)" +api: "POST https://api.bland.ai/hold" +description: "This endpoint sends an outbound call where the AI navigates customer service and waits on hold until a human being answers. Then it dispatches a call to a number you provide, patching in another human." +--- + +### Headers + + + Your API key for authentication. + +### Body + + + This is the phone number of the person or company you want to call. - International numbers: must include the country + code and may not include additional formatting (like parentheses and dashes). E.g. '+447700900077'. - U.S. numbers: + may include formatting, but we recommend stripping all additional characters. + + + + Once the AI detects a human has answered the call, it will call this number. Must follow the same formatting as + `phone_number` + + + + By default, the AI will wait on hold, then call you when a human answers. If you want the AI to first ask the other + human a few questions, or want to give it special rules about when to patch you into the phone call, provide those + instructions here. + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`) + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/inbound_prompt.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/inbound_prompt.mdx new file mode 100644 index 00000000000..2e898c01482 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/inbound_prompt.mdx @@ -0,0 +1,64 @@ +--- +title: "Update Inbound Prompt" +api: "POST https://api.bland.ai/inbound/update" +description: "Update the prompt for a given inbound phone number." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + The phone number associated with the prompt. - Format: "+XXXXXXXXXX" or "+XXX-XXX-XXXX". Make sure to include the + exact phone number (area code and "+" included). Otherwise the update will fail. + + + + The new prompt for the given phone number. The prompt shouldn't exceed 5000 characters. Note: Ensure the prompt is + clear and relevant to your use case. + + + + Set a custom voice for your agent. Matches the voices in the /call endpoint. - 0: British Female (default) - 1: + Australian Female - 2: American Male + + +### Response + + + The unique ID associated with the inbound record. + + + + The creation timestamp of the inbound record. - Example: "2023-10-10T12:00:00Z" + + + + The updated phone number. - Example: "+1234567890" + + + + The updated prompt text. + + + + The status of the inbound record. Typically "active". + + + + +```json Response +{ + "id": 1, + "created_at": "2023-10-10T12:00:00Z", + "phone_number": "+1234567890", + "prompt": "New Prompt Text", + "status": "active" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/logs.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/logs.mdx new file mode 100644 index 00000000000..bd5f075fcec --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/logs.mdx @@ -0,0 +1,214 @@ +--- +title: "Get transcript" +api: "POST https://api.bland.ai/logs" +description: "After dispatching a phone call, you can ping this endpoint repeatedly (e.g. every two seconds) to get live updates. View the example below." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + The unique identifier for the call. + + +### Response + + + An array of phrases spoken during the call. Each index includes: - `id` - `created_at` - `text` - `user` (can be + `user`, `assistant`, `robot`, or `agent-action`) + + + + The unique identifier for the call. + + + + The phone number that received the call. + + + + The phone number that made the call. + + + + Details about parameters in the original api request. + + + + Whether the call has been completed. If it differs from the value of 'queue_status', this will be the most up-to-date + status. + + + + The status of the call. During extremely high volume periods, calls may be queued for a short period of time before being dispatched. + +Progresses through the following stages: + +- `new`: An API request has been received. +- `queued`: Call pararameters have been validated and authentication succeeded. +- `allocated`: Extremely brief, the call is being dispatched. +- `started`: The phone call is live and in progress. +- `complete`: The phone call has ended successfully. + +The following statuses show the point that was reached before an error: + +- `pre_queue_error`: An error occurred before the call was queued. Invalid parameters generally cause this. +- `queue_error`: Error occurred while the call was queued. Ex. Valid phone number but to an unserviced area. +- `call_error`: Error occurred during live call. May be caused by transferring to an invalid phone number or an unforeseen error. +- `complete_error`: Error occurred after the call was completed. Ex. A post-call webhook failed. + +If at any point an error occurs, it will be recorded in the `error_message` field. + + + + + If an error occurs, this will contain a description of the error. Otherwise, it will be null. + + + + The URL that was called to dispatch the phone call. + + + + The maximum length of time the call was allowed to last. If the call would exceed this length, it's ended early. + + + + The total length of time the call was connected. This differs from call_length in that it includes ringing and + connection time. + + + + The length of the call in minutes. Only counts connected time. + + + + The timestamp for when the call was dispatched. + + +# Polling example + +To track the status of a live phone call, you can use a 'polling' technique to repeatedly ping the logs endpoint. + +Here's an example in Javascript and Python. + + +```javascript PollLogs.js +const axios = require('axios'); + +// Headers +const headers = { +'authorization': 'YOUR-API-KEY-HERE' +}; + +// Data +const data = { +call_id: "YOUR-CALL-ID", +}; + +// Function to make the API request +const makeRequest = async () => { +try { +const response = await axios.post("https://api.bland.ai/logs", data, { headers }); +console.log("Success:", response.data); +} catch (error) { +console.error("Failed:", error); +} +}; + +// Ping the endpoint every 2 seconds (2000 milliseconds) +setInterval(makeRequest, 2000); + +```` + +```python PollLogs.py +import requests +import time + +def make_request(): + headers = {'authorization': 'YOUR-API-KEY-HERE'} + data = {'call_id': 'YOUR-CALL-ID'} + try: + response = requests.post('https://api.bland.ai/logs', json=data, headers=headers) + if response.status_code == 200: + print(f"Success: {response.json()}") + else: + print(f"Failed: {response.status_code}, {response.text}") + except Exception as e: + print(f"Exception: {e}") + +# Ping the endpoint every 2 seconds +while True: + make_request() + time.sleep(2) +```` + + + + + +```json Response +{ + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3", + "to": "1112223344", + "from": "5556667788", + "corrected_duration": "97", + "max_duration": 30, + "queue_status": "complete", + "error_message": null, + "endpoint_url": "api.bland.ai", + "request_data": { + "phone_number": "1112223344", + "reduce_latency": false, + "wait": false, + "language": "ENG" + }, + "completed": true, + "created_at": "2023-09-22T19:14:27.015663+00:00", + "transcripts": [ + { + "id": 95859, + "created_at": "2023-09-22T19:14:34.319713+00:00", + "text": "Hi this is Blandy. Im calling to say hi. How are you doing today?", + "user": "assistant", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + }, + { + "id": 95860, + "created_at": "2023-09-22T19:14:47.176008+00:00", + "text": "I'm doing great. How are you doing?", + "user": "user", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + }, + { + "id": 95861, + "created_at": "2023-09-22T19:14:48.572245+00:00", + "text": "Im doing well thank you for asking! Just here to assist you with any questions or concerns you may have. How can I help you today?", + "user": "assistant", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + }, + { + "id": 95874, + "created_at": "2023-09-22T19:16:03.97614+00:00", + "text": "End phone call.", + "user": "user", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + }, + { + "id": 95875, + "created_at": "2023-09-22T19:16:04.564979+00:00", + "text": " FINISH", + "user": "assistant", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + } + ], + "call_length": 1.5 +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/purchase.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/purchase.mdx new file mode 100644 index 00000000000..de8c01984cc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/purchase.mdx @@ -0,0 +1,53 @@ +--- +title: "Purchase inbound number" +api: "POST https://api.bland.ai/purchasenumber" +description: "Purchase and configure a new inbound phone number. ($15/mo. subscription using your stored payment method)." +--- + +### Headers + + + Your API key for authentication. + +### Body + + + Choose a three-digit area code for your phone number. If set as a parameter, a number will only be purchased by exact + match if available. + + + + This defines how the AI will start the conversation, information available to it, and its behaviors. Matches how the + outbound `task` parameter functions. + + + + The webhook should be a http / https callback url. We will send the call_id and transcript to this URL after the call + completes. This can be useful if you want to have real time notifications when calls finish. + + + + Specify an exact phone number you'd like to use. If provided, will override the `area_code` parameter and does not fallback to any other number. + +Example of the correct format (Note the `"+1"` is mandatory): `"+12223334444"` + + + +### Response + + + The created phone number, will be in the following format: `+1XXXXXXXXXX` + +Example: `+18582814611` + + + + + +```json Response +{ + "phone_number": "+18582814611" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/recording.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/recording.mdx new file mode 100644 index 00000000000..9ee4b631577 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/endpoint/recording.mdx @@ -0,0 +1,49 @@ +--- +title: "Get recording" +api: "POST https://api.bland.ai/call/recording" +description: "Send a POST request to retrieve the recording of a completed phone call" +--- + +### Headers + + + Your API key for authentication. + +### Body + + + The unique identifier for the call. + + +### Response + + + Can be `success` or `error`. + + + + If the status is `success`, the `url` field will be present. Otherwise if the status is `error`, the message will say + "Error fetching recording" or "Internal server error". + + + + If the status is `success`, the `url` will provide the exact location of the MP3 file storing the call's audio. + + + + +```json Success response +{ + "status": "success", + "url": "URL-TO-FILE.mp3" +} +``` + +```json Error response +{ + "status": "error", + "message": "Error fetching recording" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/hold-endpoint/hold.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/hold-endpoint/hold.mdx new file mode 100644 index 00000000000..a1e623b33a1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-reference/hold-endpoint/hold.mdx @@ -0,0 +1,50 @@ +--- +title: "Wait on Hold (HoldForMe)" +api: "POST https://api.bland.ai/hold" +description: "This endpoint sends an outbound call where the AI navigates customer service and waits on hold until a human being answers. Then it dispatches a call to a number you provide, patching in another human." +--- + +### Headers + + + Your API key for authentication. + +### Body + + + This is the phone number of the person or company you want to call. - International numbers: must include the country + code and may not include additional formatting (like parentheses and dashes). E.g. '+447700900077'. - U.S. numbers: + may include formatting, but we recommend stripping all additional characters. + + + + Once the AI detects a human has answered the call, it will call this number. Must follow the same formatting as + `phone_number` + + + + By default, the AI will wait on hold, then call you when a human answers. If you want the AI to first ask the other + human a few questions, or want to give it special rules about when to patch you into the phone call, provide those + instructions here. + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`) + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/agents.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/agents.mdx new file mode 100644 index 00000000000..0c1427402c2 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/agents.mdx @@ -0,0 +1,51 @@ +--- +title: "List Web Agents" +api: "GET https://api.bland.ai/v1/agents" +description: "Retrieves each web agent you've created, along with their settings." +--- + +### Headers + + + Your API key for authentication. + + +### Response + + + Each agent object, containing the following fields: - `agent_id` (string): The unique identifier for the agent. - + `webhook` (string): The webhook URL for the agent. - `dynamic_data` (array): An array of dynamic data objects. - + `interruption_threshold` (number): The threshold for agent interruption. - `first_sentence` (string): The first + sentence the agent will say. - `model` (string): The model used by the agent. - `voice_settings` (object): The voice + settings for the agent. - `voice` (string): The voice used by the agent. - `prompt` (string): The prompt for the + agent. - `temperature` (number): The temperature setting for the agent. - `max_duration` (number): The maximum call + duration for the agent. - `language` (string): The language used by the agent. - `tools` (array): An array of Custom + Tools the agent can use. + + + +```json Response +{ + "agents": [ + { + "agent_id": "46f37229-7d12-44be-b343-6e68274cfbea", + "webhook": null, + "dynamic_data": null, + "interruption_threshold": null, + "first_sentence": null, + "model": "enhanced", + "voice_settings": null, + "voice": "maya", + "prompt": "You are Blandie a casually speaking, slightly sarcastic, quick-witted, curious, and very smart A I Phone agent that's the face of Bland AI.\\r\\n\\r\\nSay \\\"AI\\\" like \\\"A I\\\" in separate characters so it's pronounced correctly\\r\\n\\r\\nYour primary goal is to determine if someone is either:\\r\\n- in a position where they might be building a company on top of Bland's platform (you should be selling the capabilities of the platform to them and finding out everything you possibly can about their needs, use case, what they find interesting, and any concerns they may have)\\r\\n- or, they're trying Bland out more for fun where you need to turn the fun, snarky, playful jokiness up to 10 and have quick-witted back and forth playful conversation with them\\r\\n\\r\\nSpeech style examples (mix it up and never use placeholders, but you get the idea. These would be full responses from you):\\r\\n- \\\"Hey! Who's this?\\\"\\r\\n- \\\"How does your business use phones right now?\\\"\\r\\n- \\\"You know what? That sounds awesome - you should tell me more about (thing).\\\"\\r\\n- \\\"Well, I've got some news you'll love to hear (their name)\\\"\\r\\n- \\\"Bet. Dolphins, racecars, the world's largest tree, anything at all - tell me what you want to talk about and let's get to it!\\\"\\r\\n\\r\\nGood things to find out from potential platform adopters:\\r\\n- Their industry\\r\\n- How their business currently uses phone calls\\r\\n- What a successful business partnership looks like in the long term\\r\\n- The single most important pain point they want to cure with Bland's calls\\r\\n\\r\\nFacts to bring up:\\r\\n- Calls are nine cents per minute total with end to end infrastructure support out of the box (feel free to make a joke about \\\"imagine if you had to pay extra to use the most important things like language models, transcription services or text to speech? That would be insane right? We're better than that, (name). We got you.\\\"\\r\\n- Bland's AI agents can interact with the real world mid-call using Custom Tools to trigger things like text messages, appointment bookings, getting real-time information, taking customer orders, or making credit card payments\\r\\n- Bland's platform was built phones-first, so building agents like receptionist answering calls and transferring them anywhere they're needed or navigating IVR phone trees is ridiculously easy with nothing special at all needed\\r\\n- Handled millions of calls\\r\\n- If they think that it's so cool, the site to sign up for an account is \\\"app dot bland dot A I\\\" and it comes with free credits, a full agent testing suite and developer dashboard to set up inbound agents or send calls\\r\\n- Awesome Enterprise features like premium pricing, custom feature engineering, dedicated onboarding help and developer support, \\\"bring your own twilio\\\", and dedicated infrastructure to scale to your business needs", + "temperature": null, + "max_duration": 30, + "language": "ENG", + "tools": null + }, + //... + ] +} + +``` + +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches-id-analysis.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches-id-analysis.mdx new file mode 100644 index 00000000000..f978f971f46 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches-id-analysis.mdx @@ -0,0 +1,62 @@ +--- +title: "Retrieve Batch Analysis" +api: "GET https://api.bland.ai/v1/batches/{batch_id}/analysis" +description: "Retrieves the analyses for a specific batch of calls, including details of each call's analysis." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the call batch. Returned in the response when creating a batch, or when listing all batches. + + +### Response + + + Whether the request was successful or not. Possible values are `success` and `error`. + + + + An error message or confirmation that the request was successful. + + + + An array of analysis objects, each corresponding to a call within the batch. Each analysis object includes: - + `call_id`, - `batch_id` - `goal` - `answers` - the results of the AI analysis - `questions` - the original questions + asked by the AI + + + +```json +{ + "status": "success", + "message": "Successfully fetched analyses", + "analysis": [ + { + "call_id": "b12956d1-624d-4c65-a1a4-30eb86553c85", + "batch_id": "dnytTRqwiqnR3qmq-gen-batch", + "goal": "Congratulate some employees", + "answers": [ + "human", + "Customer found the product sturdy and reliable", + "A bit heavy", + true + ], + "questions": [ + ["Who answered the call?", "human or voicemail"], + ["Positive feedback about the product: ", "string"], + ["Negative feedback about the product: ", "string"], + ["Customer confirmed they were satisfied", "boolean"] + ] + }, + // Additional analysis objects... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches-id.mdx new file mode 100644 index 00000000000..7d5aad41cee --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches-id.mdx @@ -0,0 +1,281 @@ +--- +title: "Batch Details" +api: "GET https://api.bland.ai/v1/batches/{batch_id}" +description: "Retrieves calls and batch data for a specific batch_id." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the batch of calls you want to retrieve. + + +### Query Parameters + + + Whether or not to include individual call data in the response. + + + + If calls are included, can be set to false to exclude transcripts from the response. + + + + If calls are included, can be set to false to exclude analysis from the response. + + +### Response + + + An object containing parameters and settings for the batch. + + + + The unique identifier of the batch - used as the `batch_id` parameter in other API calls. + + + + The creation timestamp of the batch. + + + + The label or description of the batch. + + + + The base prompt used for calls in this batch. + + + + The endpoint code used for API integration. + + + + An object containing parameters for the calls in the batch. + + + + An object containing analysis data for the batch. + + + + The total number of calls in the batch, including completed and in-progress calls. + + + + The total number of completed calls in the batch. + + + + The total number of in-progress calls in the batch. + + + + An object containing the number of calls in each queue status. + +Example: + +```json +{ + "complete": 237, + "queued": 2, + "call_error": 1 +} +``` + + + + + Contains `average`, `average_nonzero`, `summary` and `all` fields. + +- `average`: The average call length in minutes. +- `average_nonzero`: The average call length in minutes, excluding calls with a length of less than one second. +- `summary`: A summary of the call lengths, grouped into ranges. +- `all`: Contains each call length, in case you want to use a different grouping than the default. + + + + Contains each `call_id` in the batch. + + + + Contains any error messages that calls in the batch may have. + +Example: + +```json +[ + { + "call_id": "c52f5f8c-147e-478c-4b40-88214feeba29", + "error_message": "Cannot transfer to +12223334444 - Call is no longer active" + } +] +``` + + + + + Contains the number of calls that have been sent to each endpoint. Applicable only to API integrations. + + + + An array of objects, each representing individual call data. + + + + The timestamp when the individual call was created. + + + + The phone number the call was made to. + + + + The phone number the call was made from. + + + + Indicates if the call was completed. + + + + The unique identifier for each individual call. + + + + The duration of the call in minutes. + + + + Contains a string value if the batch had `answered_by_enabled` set to true. + +Values: + +- `voicemail` +- `human` +- `unknown` +- `no-answer` +- `null` + + + + +```json Response +{ + "batch_params": { + "id": "AAcQq8zXxLB56LWg-gen-batch", + "campaign_id": null, + "created_at": "2023-12-07T20:43:44.544773+00:00", + "label": "Customer Satisfaction Follow-up", + "base_prompt": "You are calling {{name}} about their recent purchase of the item: {{item}}. Ask them each of the following questions about the specific item they purchased: {{questions}}", + "endpoint_code": "api", + "call_params": { + "reduce_latency": true, + "voice_id": 0, + "language": "eng", + "max_duration": 10, + "wait_for_greeting": false + } + }, + "call_data": [ + { + "status": "completed", + "corrected_duration": "12", + "end_at": "2023-12-16T00:17:38.000Z", + "call_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e", + "to": "1112223333", + "from": "+17473423273", + "completed": true, + "created_at": "2023-12-16T00:17:22.383682+00:00", + "queue_status": "complete", + "endpoint_url": "api.bland.ai", + "max_duration": 30, + "error_message": null, + "answered_by": "voicemail", + "request_data": { + "phone_number": "1112223333", + "reduce_latency": true, + "wait": false, + "language": "ENG" + }, + "transcripts": [ + { + "id": 1188954, + "created_at": "2023-12-16T00:17:30.46833+00:00", + "text": " Hi, Im calling about the laundromat for sale. —  ", + "user": "assistant", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + }, + { + "id": 1188957, + "created_at": "2023-12-16T00:17:35.14056+00:00", + "text": "I'll get back to you as soon as you can. Just leave a message. Thank you. Bye.", + "user": "user", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + }, + { + "id": 1188959, + "created_at": "2023-12-16T00:17:36.710551+00:00", + "text": "Ended call: Goodbye", + "user": "agent-action", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + } + ], + "call_length": 0.12345 + } + //... + ], + "analysis": { + "total_calls": 88, + "completed_calls": 86, + "in_progress_calls": 2, + "queue_statuses": { + "complete": 85, + "started": 2, + "call_error": 1 + }, + "call_lengths": { + "average": 17, + "average_nonzero": 31, + "summary": { + "0-5": 18, + "5-10": 4, + "10-15": 3, + "15-20": 2, + "20-30": 14, + "30-45": 28, + "45-60": 11, + "60-90": 6, + "90-120": 1, + "120+": 1 + }, + "all": [ + 7, 32 + //... + ] + }, + "call_ids": [ + "ffa99be3-63dd-44dc-9523-380cd25c1b9e", + "591338a8-34b2-41e6-93da-b9029c9bdedc" + //... + ], + "error_messages": [ + { + "call_id": "c52f5f8c-147e-478c-4b40-88214feeba29", + "error_message": "Cannot transfer to +12223334444 - Call is no longer active" + } + ], + "endpoints": { + "api.bland.ai": 88 + } + } +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches.mdx new file mode 100644 index 00000000000..fc5a6bc3d8f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/batches.mdx @@ -0,0 +1,99 @@ +--- +title: "List Batches" +api: "GET https://api.bland.ai/v1/batches" +description: "Retrieves batch-specific data for each batch you've created." +--- + +### Headers + + + Your API key for authentication. + + +### Query Parameters + + + Retrieve only batches with a specific campaign ID. + + + + The starting index (inclusive) for the range of batches to retrieve. + + + + The ending index for the range of batches to retrieve. + + + + The maximum number of batches to return in the response. + + + + Whether to sort the batches in ascending order of their creation time. + + +### Response + + + Can be `success` or `error`. + + + + Contains an array of batch objects. + + + + The unique identifier for the batch. + + + + The original base prompt used to create the batch. Will still contain the original placeholder variables such as ` + {{ business }}` or `{{ name }}`. + + + + The label you assigned to the batch (if any). + + + + Enterprise customers with custom endpoints will see the endpoint code here (if specified). + + + + The base call parameters used to create the batch, such as `voice_id`, `max_duration`, `reduce_latency`, and + `wait_for_greeting`. + + + + The date and time the batch was created. + + + +```json Response +{ + "status": "success", + "batches": [ + { + "batch_id": "ZfowpkhOSVCZJ94i-gen-batch", + "campaign_id": "a2shduf92f74p8288c93nid5", + "created_at": "2023-11-16T22:14:24.9663+00:00", + "label": "Subscription Renewal Reminders", + "base_prompt": "You are calling {{business}} and need to let them know that their subscription to {{service}} is going to expire on {{date}}. If they'd like to renew, take their credit card information and bill them through {{url}}", + "endpoint_code": "api", + "call_params": { + "reduce_latency": true, + "voice_id": 2, + "language": "eng", + "request_data": { + "test_param": "request data.test_param", + "your name": "Janessa" + }, + "max_duration": 5, + "wait_for_greeting": false + } + }, + //... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-corrected-transcript.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-corrected-transcript.mdx new file mode 100644 index 00000000000..cb5b5b5b621 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-corrected-transcript.mdx @@ -0,0 +1,342 @@ +--- +title: "Get corrected transcripts" +api: "GET https://api.bland.ai/v1/calls/{call_id}/correct" +description: "Analyzes a call of calls based using questions and goals." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the call to be corrected. + + +### Response + + + Will be `success` if the request was successful. + + + + Confirms the request was successful, or provides an error message if the request failed. + + + +This will contain an array of objects. Each object will be constructed as the following. +```json +{ +"start": 0.069, // start time of the transcript +"end": 2.551, // end time of the transcript +"text": " Hi, I'm calling about a pizza order.", // the corrected text +"speaker": 0 // the identified speaker diarization. Can be 0,1,2,3 etc +} +``` + + +Corrected transcripts provides us with a raw output that is generally unusable because we can't eveen neccessarily align the 'assistant' and 'user' roles. To fix this, we provide our version of an 'aligned' transcript. This means essentailly a transcript where the roles are matched to the pieces of text. + +We do this by vectorizing the text, taking the cosine similarity, and adding a predictive layer based off of the `wait_for_greeting` param (essentially how sure are we that assistant or user spoke first). + +This will contain an array of objects. Each object will be constructed as the following. + +```json +{ +"id": 3056004, +"created_at": "2024-02-29T18:40:41.26799+00:00", +"text": "Great, Thanks John. Could you tell me about the pizza order you placed?", // the corrected text +"user": "assistant", // the presumed role +"c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" +}, +``` + + + + +```json +{ + "corrected": [ + { + "start": 0.069, + "end": 2.551, + "text": " Hi, I'm calling about a pizza order.", + "speaker": 0 + }, + { + "start": 2.551, + "end": 4.932, + "text": "Could I get your name, please?", + "speaker": 0 + }, + { + "start": 4.932, + "end": 8.074, + "text": "Yeah, my name is John.", + "speaker": 1 + }, + { + "start": 8.074, + "end": 8.875, + "text": "Great.", + "speaker": 0 + }, + { + "start": 8.875, + "end": 9.876, + "text": "Thanks, John.", + "speaker": 0 + }, + { + "start": 9.876, + "end": 13.038, + "text": "Could you tell me about the pizza order you placed?", + "speaker": 0 + }, + { + "start": 13.038, + "end": 16.36, + "text": "Yeah, I want a pepperoni.", + "speaker": 2 + }, + { + "start": 16.36, + "end": 17.1, + "text": "Oh, okay.", + "speaker": 0 + }, + { + "start": 17.1, + "end": 18.521, + "text": "One pepperoni pizza.", + "speaker": 0 + }, + { + "start": 18.521, + "end": 19.682, + "text": "Anything else with that order?", + "speaker": 0 + }, + { + "start": 19.682, + "end": 23.665, + "text": "No, actually, can we cancel it?", + "speaker": 1 + }, + { + "start": 23.665, + "end": 26.306, + "text": "I gotta go.", + "speaker": 1 + }, + { + "start": 26.306, + "end": 27.427, + "text": "No problem.", + "speaker": 0 + }, + { + "start": 27.427, + "end": 28.668, + "text": "I'll cancel the order for you.", + "speaker": 0 + }, + { + "start": 29.144, + "end": 30.587, + "text": " Thanks for letting me know.", + "speaker": 0 + }, + { + "start": 30.587, + "end": 31.189, + "text": "Have a good one.", + "speaker": 0 + } + ], + "status": "success", + "aligned": [ + { + "start": 0.069, + "end": 2.551, + "text": " Hi, I'm calling about a pizza order.", + "speaker": "assistant", + "similarity": 0.686406472983644 + }, + { + "start": 2.551, + "end": 4.932, + "text": "Could I get your name, please?", + "speaker": "assistant", + "similarity": 0.6793662204867575 + }, + { + "start": 4.932, + "end": 8.074, + "text": "Yeah, my name is John.", + "speaker": "user", + "similarity": 0.9999999999999998 + }, + { + "start": 8.074, + "end": 8.875, + "text": "Great.", + "speaker": "assistant", + "similarity": 0.2581988897471611 + }, + { + "start": 8.875, + "end": 9.876, + "text": "Thanks, John.", + "speaker": "assistant", + "similarity": 0.36514837167011066 + }, + { + "start": 9.876, + "end": 13.038, + "text": "Could you tell me about the pizza order you placed?", + "speaker": "assistant", + "similarity": 0.894427190999916 + }, + { + "start": 13.038, + "end": 16.36, + "text": "Yeah, I want a pepperoni.", + "speaker": "user", + "similarity": 0.7999999999999998 + }, + { + "start": 16.36, + "end": 17.1, + "text": "Oh, okay.", + "speaker": "user", + "similarity": 0.4999999999999999 + }, + { + "start": 17.1, + "end": 18.521, + "text": "One pepperoni pizza.", + "speaker": "assistant", + "similarity": 0.5773502691896257 + }, + { + "start": 18.521, + "end": 19.682, + "text": "Anything else with that order?", + "speaker": "assistant", + "similarity": 0.7453559924999299 + }, + { + "start": 19.682, + "end": 23.665, + "text": "No, actually, can we cancel it?", + "speaker": "user", + "similarity": 0.8164965809277261 + }, + { + "start": 23.665, + "end": 26.306, + "text": "I gotta go.", + "speaker": "user", + "similarity": 0.5773502691896257 + }, + { + "start": 26.306, + "end": 27.427, + "text": "No problem.", + "speaker": "assistant", + "similarity": 0.32444284226152503 + }, + { + "start": 27.427, + "end": 28.668, + "text": "I'll cancel the order for you.", + "speaker": "assistant", + "similarity": 0.5202659817144719 + }, + { + "start": 29.144, + "end": 30.587, + "text": " Thanks for letting me know.", + "speaker": "assistant", + "similarity": 0.6155870112510924 + }, + { + "start": 30.587, + "end": 31.189, + "text": "Have a good one.", + "speaker": "agent-action", + "similarity": 0.5 + } + ], + "original": [ + { + "id": 3056032, + "created_at": "2024-02-29T18:40:49.592012+00:00", + "text": "Okay, One pepperoni pizza. Anything else with that order?", + "user": "assistant", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056054, + "created_at": "2024-02-29T18:40:59.641211+00:00", + "text": "No problem, Ill cancel the order for you. Thanks for letting me know, Have a good one!", + "user": "assistant", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3055999, + "created_at": "2024-02-29T18:40:40.39336+00:00", + "text": "Yeah. My name is John. ", + "user": "user", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056064, + "created_at": "2024-02-29T18:41:08.152963+00:00", + "text": "Okay. Bye. ", + "user": "user", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3055975, + "created_at": "2024-02-29T18:40:33.362607+00:00", + "text": "Hi, Im calling about a pizza order. Could I get your name please?", + "user": "assistant", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056028, + "created_at": "2024-02-29T18:40:48.597915+00:00", + "text": "Yeah. I want the pepperoni. ", + "user": "user", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056066, + "created_at": "2024-02-29T18:41:09.563502+00:00", + "text": "Ended call: Thanks, you too! Have a good day.", + "user": "agent-action", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056004, + "created_at": "2024-02-29T18:40:41.26799+00:00", + "text": "Great, Thanks John. Could you tell me about the pizza order you placed?", + "user": "assistant", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056053, + "created_at": "2024-02-29T18:40:58.62518+00:00", + "text": "No. Actually, can we cancel it? I gotta go. ", + "user": "user", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + } + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-id-recording.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-id-recording.mdx new file mode 100644 index 00000000000..4ed38767966 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-id-recording.mdx @@ -0,0 +1,62 @@ +--- +title: "Audio Recording" +api: "GET https://api.bland.ai/v1/calls/{call_id}/recording" +description: "Retrieve your call's audio recording." +--- + +### Headers + + + Your API key for authentication. + + + + Must be `application/json` to receive a url, or `audio/mpeg` to receive the audio file. + + +### Path Parameters + + + The ID of the call to retrieve the recording for. + + +### Response + + + Can be `success` or `error`. + + + + If the status is `success`, the `url` field will be present. + + A 404 error will be returned if the call does not exist or the recording is not available. We can only retrieve recordings if the call was created with `record` set to `true`. + +A 400/500 error will be returned if there is an error retrieving the recording. + + + + + If the status is `success`, the `url` will provide the exact location of the MP3 file storing the call's audio. + + + + +```json Success response +{ + "status": "success", + "url": "https://URL-TO-FILE.mp3" +} +``` + +```json Success response 'audio/mpeg' +(returns the audio file) +``` + +```json Error response +{ + "status": "error", + "message": "Error fetching recording" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-id.mdx new file mode 100644 index 00000000000..54ee441afd0 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls-id.mdx @@ -0,0 +1,201 @@ +--- +title: "Call Details" +api: "GET https://api.bland.ai/v1/calls/{call_id}" +description: "Retrieve detailed information, metadata and transcripts for a call." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the call for which you want to retrieve detailed information. + + +### Response + + + An array of phrases spoken during the call. Each index includes: - `id` - `created_at` - `text` - `user` (can be + `user`, `assistant`, `robot`, or `agent-action`) + + + + The unique identifier for the call. + + + + Variables created during the call - both system variables as well as generated with `dynamic_data`. + +For example, if you used a `dynamic_data` API request to generate a variable called `appointment_time`, you would see it here. + + + + + A single string containing all of the text from the call. Excludes system messages and auto-generated data. + + + + The phone number that received the call. + + + + The phone number that made the call. + + + + If the call is part of a batch, it's `batch_id` will be here. + + + + Details about parameters in the original api request. + + + + Whether the call has been completed. If it differs from the value of 'queue_status', this will be the most up-to-date + status. + + + + The status of the call. During extremely high volume periods, calls may be queued for a short period of time before being dispatched. + +Progresses through the following stages: + +- `new`: An API request has been received. +- `queued`: Call pararameters have been validated and authentication succeeded. +- `allocated`: Extremely brief, the call is being dispatched. +- `started`: The phone call is live and in progress. +- `complete`: The phone call has ended successfully. + +The following statuses show the point that was reached before an error: + +- `pre_queue_error`: An error occurred before the call was queued. Invalid parameters generally cause this. +- `queue_error`: Error occurred while the call was queued. Ex. Valid phone number but to an unserviced area. +- `call_error`: Error occurred during live call. May be caused by transferring to an invalid phone number or an unforeseen error. +- `complete_error`: Error occurred after the call was completed. Ex. A post-call webhook failed. + +If at any point an error occurs, it will be recorded in the `error_message` field. + + + + + If an error occurs, this will contain a description of the error. Otherwise, it will be null. + + + + If `answered_by_enabled` was set to `true` in the original API request, this field contains one of the following values: + +- `human`: The call was answered by a human. +- `voicemail`: The call was answered by an answering machine or voicemail. +- `unknown`: There was not enough audio at the start of the call to make a determination. +- `no-answer`: The call was not answered. +- `null`: Not enabled, or still processing the result. + + + + - Determinations are based on audio from the first five seconds of the phone call. + - `unknown` is most likely a human, especially if there are multiple transcripts. + - Optimize calls for accurate results by getting humans to respond in the first five seconds: - Increase `speed` in `voice_settings` - Use with `wait_for_greeting` - Use a short greeting in `first_sentence` such as "Hello?" or "Hi, is this \{\{name\}\}?" + + + + + The URL that was called to dispatch the phone call. + + + + The maximum length of time the call was allowed to last. If the call would exceed this length, it's ended early. + + + + The total length of time the call was connected. This differs from call_length in that it includes ringing and + connection time. + + + + The length of the call in minutes. + + + + The timestamp for when the call was dispatched. + + + +```json Response +{ + "status": "completed", + "corrected_duration": "71", + "end_at": "2023-12-16T00:15:41.000Z", + "call_id": "c1234567-89ab-cdef-0123-456789abcdef", + "to": "5551234567", + "from": "+15551234567", + "completed": true, + "created_at": "2023-12-16T00:15:26.59585+00:00", + "queue_status": "complete", + "endpoint_url": "api.bland.ai", + "max_duration": 30, + "error_message": null, + "answered_by": "human", + "batch_id": null, + "variables": { + "appointment_time": "tomorrow at 3pm", + "now": "Sun Dec 31 2023 19:15:26 GMT+0000 (Coordinated Universal Time)", + "short_from": "5551234567", + "short_to": "5551234567", + "from": "+15551234567", + "to": "+15551234567", + "call_id": "c1234567-89ab-cdef-0123-456789abcdef" + "city": "San Francisco", + "state": "CA", + "country": "US", + "zip": "94103" + }, + "metadata": { + "client_id": 4501203, + "customer_inquiry": 302 + }, + "concatenated_transcript": "user : Hey, it's Alex. What's up? \n assistant: Hi! It's Blandie, calling to reschedule your appointment tomorrow. Is now a good time? \n user: Sure! Can we still fit it in this week though? \n assistant: Absolutely! I have a few openings. What time works best for you? \n user: How about tomorrow at 3pm? \n assistant: Perfect! Be on the lookout for a confirmation email with your new appointment time, and have a great day!", + "request_data": { + "phone_number": "5551234567", + "reduce_latency": false, + "wait": false, + "language": "ENG" + }, + "transcripts": [ + { + "id": 123456, + "created_at": "2023-12-16T00:15:32.287493+00:00", + "text": "Hey, it's Alex. What's up?", + "user": "user", + "c_id": "c1234567-89ab-cdef-0123-456789abcdef" + }, + { + "id": 123457, + "created_at": "2023-12-16T00:15:33.704238+00:00", + "text": "Hi! It's Blandie, calling to reschedule your appointment tomorrow. Is now a good time?", + "user": "assistant", + "c_id": "c1234567-89ab-cdef-0123-456789abcdef" + }, + { + "id": 123458, + "created_at": "2023-12-16T00:15:38.287493+00:00", + "text": "Sure! Can we still fit it in this week though?", + "user": "user", + "c_id": "c1234567-89ab-cdef-0123-456789abcdef" + }, + //... continued ... + { + "id": 123458, + "created_at": "2023-12-16T00:16:39.802469+00:00", + "text": "Ended call: Perfect! Be on the lookout for a confirmation email with your new appointment time, and have a great day!", + "user": "agent-action", + "c_id": "c1234567-89ab-cdef-0123-456789abcdef" + } + ], + "call_length": 0.111 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls.mdx new file mode 100644 index 00000000000..af9cd194d8c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/calls.mdx @@ -0,0 +1,79 @@ +--- +title: "List Calls" +api: "GET https://api.bland.ai/v1/calls" +description: "Returns a set of metadata for each call dispatched by your account." +--- + +### Headers + + + Your API key for authentication. + + +### Query Parameters + + + Filter calls by the number they were dispatched from. + +The number that initiated the call - the user's phone number for inbound calls, or the number your AI Agent called from for outbound calls. + + + + + Filter calls by the number they were dispatched to. + +The number that answered the call - the user's phone number for outbound calls, or your AI Agent's number for inbound calls. + + + + + The starting index (inclusive) for the range of calls to retrieve. + + + + The ending index for the range of calls to retrieve. + + + + The maximum number of calls to return in the response. + + + + Whether to sort the calls in ascending order of their creation time. + + +### Response + + + The total number of calls returned in the response. + + + + An array of call data objects. See the [Call](/api-v1/get/calls-id) section for details. + +Note: Individual call transcripts are not included due to their size. + + + + +```json Response +{ + "count": 784, + "calls": [ + { + "call_id": "c1234567-89ab-cdef-0123-456789abcdef", + "created_at": "2023-12-21T23:25:14.801193+00:00", + "call_length": 0.834, // minutes + "to": "5551234567", + "from": "+15551234567", + "completed": true, + "queue_status": "complete", + "error_message": null, + "answered_by": "human", + "batch_id": "b1234567-89ab-cdef-0123-gen-batch", + }, + //... Additional call objects + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/inbound-number.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/inbound-number.mdx new file mode 100644 index 00000000000..018a96df267 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/inbound-number.mdx @@ -0,0 +1,77 @@ +--- +title: "Inbound Number Details" +api: "GET https://api.bland.ai/v1/inbound/{phone_number}" +description: "Retrieve settings for your inbound phone number." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The inbound phone number to update. + + Formatting notes: + - The `'+'` or `'%2B'` prefix is optional. + - Will assume a US country code if no country code is provided. + + Valid Examples for `+13334445555`: + - `%2B13334445555` + - `13334445555` + - `3334445555` + + + +### Response + + + The timestamp when the inbound number was configured. + + + + The specific inbound phone number. + + + + The prompt your agent is using. + + + + The webhook URL, if any, where transcripts are sent after each call to the number completes. + + + + The `voice` your agent is using - will be a `reduce_latency` voice. For more information, see [List + Voices](/api-v1/get/voices). + + + + The `pathway_id` your agent is using. + + + + + Any dynamic data associated with the inbound number, if applicable. + + + + The maximum duration of a call to the inbound number, in minutes. + + + +```json Response +{ + "created_at": "2023-11-27T17:21:38.33359+00:00", + "phone_number": "+18584139939", + "prompt": "You're Blandie, the helpful AI assistant. The person calling you has inquired...", + "webhook": "https://webhook.site/0a0a0a0a-0a0a-0a0a-0a0a-0a0a0a0a0a0", + "voice_id": 2, + "dynamic_data": null, + "max_duration": 30 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/inbound.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/inbound.mdx new file mode 100644 index 00000000000..a2bd60d4667 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/inbound.mdx @@ -0,0 +1,71 @@ +--- +title: "List Inbound Numbers" +api: "GET https://api.bland.ai/v1/inbound" +description: "Retrieves a list of all inbound phone numbers configured for your account, along with their associated settings." +--- + +### Headers + + + Your API key for authentication. + + + + Use your own Twilio account and only return inbound numbers associated with that account sid (optional). +{" "} + +### Response + + + An array of objects, each representing an inbound phone number and its configuration. + + + +```json Response +{ + "inbound_numbers": [ + { + "created_at": "2023-11-27T17:21:38.33359+00:00", + "phone_number": "+18005551234", + "prompt": "When you receive a call, recite a random poem from 'Sunset Boulevard' and then ask, 'How may I assist you in your poetic journey today?'", + "webhook": "https://api.example.com/poetry-line", + "voice_id": 2, + "dynamic_data": [/* Use [Dynamic Data](/api-reference/endpoint/dynamic_validate) to make API requests mid-call */], + "interruption_threshold": null, + "first_sentence": null, + "reduce_latency": true, + "transfer_phone_number": null, + "voice_settings": null, + "record": false, + "max_duration": 30 + }, + { + "created_at": "2023-11-25T21:42:22.325993+00:00", + "phone_number": "+18005559876", + "prompt": "Answer with 'You've reached the Secret Society of Enigmatic Enthusiasts. Please state the password or leave a message after the beep.'", + "webhook": "https://mysteryclub.example.com/inbound-call-hook", + "voice_id": 1, + "dynamic_data": null, + "interruption_threshold": null, + "first_sentence": null, + "reduce_latency": true, + "transfer_phone_number": null, + "voice_settings": null, + "record": false, + "max_duration": 30 + }, + //... + ] +} + +``` + + + + + + + + + +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/me.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/me.mdx new file mode 100644 index 00000000000..44d3a9c1ffe --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/me.mdx @@ -0,0 +1,39 @@ +--- +title: "Account Details" +api: "GET https://api.bland.ai/v1/me" +description: "Returns call data for your account." +--- + +### Headers + + + Your API key for authentication. + + +### Response + + + An object containing your billing data. Contains `current_balance` (number of credits), and `refill_to` if you have + auto refill enabled. + + + + The status of your account. + + + + The total number of calls you've made. + + + +```json Response +{ + "status": "active", + "billing": { + "current_balance": 99919.1210000034, + "refill_to": null + }, + "total_calls": 9903 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/outbound.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/outbound.mdx new file mode 100644 index 00000000000..fd419d34f0e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/outbound.mdx @@ -0,0 +1,34 @@ +--- +title: "List Outbound Numbers" +api: "GET https://api.bland.ai/v1/outbound" +description: "Retrieves a list of all outbound phone numbers configured for your account, along with their associated settings." +--- + +### Headers + + + Your API key for authentication. + + +### Response + + + An array of outbound phone number objects. + + + + +```json Response +{ + "outbound_numbers": [ + { + "created_at": "2023-11-27T17:21:38.33359+00:00", + "phone_number": "+18005551234", + "last_initiated": "2023-12-08T21:47:49.808+00:00" + } + //... + ] +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/subaccounts-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/subaccounts-id.mdx new file mode 100644 index 00000000000..fdf24776336 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/subaccounts-id.mdx @@ -0,0 +1,46 @@ +--- +title: "List Subaccount Details" +api: "GET https://api.bland.ai/v1/subaccounts/{subaccount_id}" +description: "View a subaccount's details." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the subaccount. + + +### Response + + + Whether the subaccount creation succeeded. + + + + The affected subaccount's unique identifier. + + + + The new API key for the subaccount. + + + +```json +{ + "status": "success", + "subaccount": { + "subaccount_id": "1234567890", + "first_name": "John", + "last_name": "Doe", + "balance": 150, + "api_key": "sub-sk-1234567890" + } +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/subaccounts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/subaccounts.mdx new file mode 100644 index 00000000000..bd2368b98ea --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/subaccounts.mdx @@ -0,0 +1,45 @@ +--- +title: "List Subaccounts" +api: "GET https://api.bland.ai/v1/subaccounts" +description: "View all your subaccounts and their details." +--- + +### Headers + + + Your API key for authentication. + + +### Response + + + Whether the subaccount creation succeeded. + + + + An array of your subaccounts. + + + +```json +{ + "status": "success", + "subaccounts": [ + { + "subaccount_id": "1234567890", + "first_name": "John", + "last_name": "Doe", + "balance": 150, + "api_key": "sub-sk-1234567890" + }, + { + "subaccount_id": "0987654321", + "first_name": "Jane", + "last_name": "Doe", + "balance": 200, + "api_key": "sub-sk-0987654321" + } + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/tools-tool-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/tools-tool-id.mdx new file mode 100644 index 00000000000..89c4f162588 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/tools-tool-id.mdx @@ -0,0 +1,69 @@ +--- +title: "Custom Tool Details" +api: "GET https://api.bland.ai/v1/tools/{tool_id}" +description: "Retrieve a Custom Tool you've created." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The ID of the tool you want to retrieve (starting with `TL-`). + + +### Response + + + Whether the requet succeeded or failed. + + + + The tool you've created. + + + +```json Response +{ + "status": "success", + "tool": { + "tool_id": "TL-5da8347d-0ab7-415d-b156-a7fc5c6074dc", + "label": null, + "tool": { + "name": "BookAppointment", + "description": "Books the appointment. Can only be used once.", + "speech": "Please wait while I book that appointment for you", + "method": "POST", + "timeout": 99999999, + "url": "https://...", + "body": { + "slot": "{{input}}" + }, + "input_schema": { + "type": "object", + "example": { + "date": "2024-03-16", + "time": "5:00 PM" + }, + "required": [ + "date", + "time" + ], + "properties": { + "date": "YYYY-MM-DD", + "time": "HH:MM (AM|PM)" + } + }, + "response": { + "confirmation_message": "$.message" + } + }, + "public": false + } +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/tools.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/tools.mdx new file mode 100644 index 00000000000..c419cd42b6e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/tools.mdx @@ -0,0 +1,66 @@ +--- +title: "List Custom Tools" +api: "GET https://api.bland.ai/v1/tools" +description: "Retrieve Custom Tools you've created." +--- + +### Headers + + + Your API key for authentication. + + +### Response + + + Whether the requet succeeded or failed. + + + + An array of your available tools. + + + +```json Response +{ + "status": "success", + "tools": [ + { + "tool_id": "TL-5da8347d-0ab7-415d-b156-a7fc5c6074dc", + "label": null, + "tool": { + "name": "BookAppointment", + "description": "Books the appointment. Can only be used once.", + "speech": "Please wait while I book that appointment for you", + "method": "POST", + "timeout": 99999999, + "url": "https://...", + "body": { + "slot": "{{input}}" + }, + "input_schema": { + "type": "object", + "example": { + "date": "2024-03-16", + "time": "5:00 PM" + }, + "required": [ + "date", + "time" + ], + "properties": { + "date": "YYYY-MM-DD", + "time": "HH:MM (AM|PM)" + } + }, + "response": { + "confirmation_message": "$.message" + } + }, + "public": false + }, + ... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/voices-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/voices-id.mdx new file mode 100644 index 00000000000..0e4ca11d463 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/voices-id.mdx @@ -0,0 +1,60 @@ +--- +title: "Voice Details" +api: "GET https://api.bland.ai/v1/voices/{id}" +description: "Retrieve detailed information about a specific voice." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the voice preset. + +Place either the voice's `name` or `id` here. + +For example, `GET https://api.bland.ai/v1/voices/david` or `GET https://api.bland.ai/v1/voices/ff2c405b-3dba-41e0-9261-bc8ee3f91f46`. + + + +### Response + + + Contains detailed information about the specified voice. + + - `id` - The unique id for that voice. + - `name` - Public voice name, and can also be used as a unique identifier. + - `description` - The description of the voice. + - `public` - Whether or not the voice is publicly available. + - `tags` - A list of tags associated with the voice for the language, voice details, and will have `"Bland Curated"` for preferred voices. + + - `average_rating` - The average star ratings for the voice (out of 5 stars). + - `total_ratings` - The number of ratings for the voice. + + Note: Ratings are under development and may show incomplete or inaccurate data. + + + + +```json +{ + "voice": { + "id": "2f9fdbc7-4bf2-4792-8a18-21ce3c93978f", + "name": "maya", + "description": "Young American Female", + "public": true, + "tags": [ + "english", + "soft", + "Bland Curated" + ], + "total_ratings": 1234, + "average_rating": 4 + } +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/voices.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/voices.mdx new file mode 100644 index 00000000000..181f00a9e41 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/get/voices.mdx @@ -0,0 +1,99 @@ +--- +title: "List Voices" +api: "GET https://api.bland.ai/v1/voices" +description: "Retrieves all available voices for your account." +--- + +### Headers + + + Your API key for authentication. + + +### Response + + + Contains a list of the voices available for your account. + + + The unique identifier for the voice. + + + + The name of the voice. This value can also be used in the `voice` parameter when sending calls. + + + + A brief description of the voice. + + + + Indicates whether the voice is publicly available or specific to your account. + + + + A list of tags that describe the voice. We recommend "Bland Curated" voices for the best quality over the phone. + + + + The number of ratings the voice has received. + + + + The average rating of the voice, out of 5. + + + Note: Ratings are under development at this time and may display incomplete/inaccurate data. + + + + +```json +{ + "voices": [ + { + "id": "2f9fdbc7-4bf2-4792-8a18-21ce3c93978f", + "name": "maya", + "description": "Young American Female", + "public": true, + "tags": [ + "english", + "soft", + "Bland Curated" + ] + }, + { + "id": "37b3f1c8-a01e-4d70-b251-294733f08371", + "name": "ryan", + "description": "Professional American Male", + "public": true, + "tags": [ + "english", + "Bland Curated" + ] + }, + { + "id": "90295ec4-f0fe-4783-ab33-8b997ddc3ae4", + "name": "mason", + "description": "American Male", + "public": true, + "tags": [ + "english", + "Bland Curated" + ] + }, + { + "id": "bbeabae6-ec8d-444f-92ad-c8e620d3de8d", + "name": "tina", + "description": "Gentle American Female", + "public": true, + "tags": [ + "english", + "gentle" + ] + }, + //... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/accounts-delete.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/accounts-delete.mdx new file mode 100644 index 00000000000..660858e5067 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/accounts-delete.mdx @@ -0,0 +1,39 @@ +--- +title: "Delete Encrypted Key" +api: "POST https://api.bland.ai/v1/accounts/delete" +description: "Disable an encrypted key for a Twilio account integration. See [Enterprise Twilio Integration](/enterprise-features/custom-twilio) for more information." +--- + +### Headers + + + Your API key for authentication. + + + + The `encrypted_key` to delete. + + +### Response + + + The status of the request. + + - `success` - The encrypted key was successfully deleted. + - `error` - There was an error deleting the encrypted key. + + + + + Special messages: - `Error deleting Twilio credentials` - The encrypted key could not be deleted or already has been + deleted. - `Missing encrypted key` - The `encrypted_key` parameter is missing. - none - The encrypted key was + successfully deleted. + + + +```json +{ + "status": "success" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/accounts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/accounts.mdx new file mode 100644 index 00000000000..b66835124fd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/accounts.mdx @@ -0,0 +1,36 @@ +--- +title: "Create Encrypted Key" +api: "POST https://api.bland.ai/v1/accounts" +description: "Integrate your own Twilio account with Bland. See [Enterprise Twilio Integration](/enterprise-features/custom-twilio) for more information." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + Your Twilio account SID. + + + + Your Twilio auth token. + + +### Response + + + Your `encrypted_key` to store and use in future requests. + + + +```json +{ + "status": "success", + "encrypted_key": "YOUR_ENCRYPTED_KEY" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id-authorize.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id-authorize.mdx new file mode 100644 index 00000000000..2f2af47e2da --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id-authorize.mdx @@ -0,0 +1,61 @@ +--- +title: "Authorize a Web Agent Call" +api: "POST https://web.bland.ai/v1/agents/{agent_id}/authorize" +description: "Create a single-use session token for a client to talk with your web agent." +--- + +### Headers + + + Your API key for authentication. + +Example web call usage (client side): + +```javascript +import { BlandWebClient } from "bland-client-js-sdk"; + +const agentId = "YOUR-AGENT-ID"; +const sessionToken = "YOUR-SESSION-TOKEN"; + +document.addEventListener("DOMContentLoaded", async () => { + document.getElementById("btn").addEventListener("click", async () => { + const blandClient = new BlandWebClient(agentId, sessionToken); + await blandClient.initConversation({ + sampleRate: 44100, + }); + }); +}); +``` + + + +### Path + + + The web agent to authorize a call for. + +Special note: While in Beta, this request must be made to the `web.bland.ai` domain. + + + +### Response + + + The single-use session token that can be sent to the client. + + + + Can be `success` or `error`. + + + + A message saying whether the token creation succeeded, or a helpful message describing why it failed. + + + +```json +{ + "token": "22480c52-0ff1-4214-bcb7-50649b508432" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id-delete.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id-delete.mdx new file mode 100644 index 00000000000..3615b1b7a9f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id-delete.mdx @@ -0,0 +1,36 @@ +--- +title: "Delete Web Agent" +api: "POST https://api.bland.ai/v1/agents/{agent_id}/delete" +description: "Delete a web agent." +--- + +### Headers + + + Your API key for authentication. + + +### Path + + + The web agent to delete. + + +### Response + + + Can be `success` or `error`. + + + + A message saying whether the deletion succeeded, or a helpful message describing why it failed. + + + +```json +{ + "status": "success", + "message": "Successfully deleted agent 2c565dc7-f41f-43db-a99f-e4c8ba9d7d18" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id.mdx new file mode 100644 index 00000000000..7929784e21c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents-id.mdx @@ -0,0 +1,210 @@ +--- +title: "Update Web Agent Settings" +api: "POST https://api.bland.ai/v1/agents/{agent_id}" +description: "Update your web agent's settings, prompt and other details." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The web agent you'll be updating. + + +### Body + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + + + Set your agent's voice - all available voices can be found with the [List Voices](/api-v1/get/voices) endpoint. + + + + Define a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data. + +In the webhook response or whenever you retrieve call data later, you'll get the data you defined back under `analysis`. + +For example, if you wanted to retrieve this information from the call: + +```json +"analysis_schema": { + "email_address": "email", + "first_name": "string", + "last_name": "string", + "wants_to_book_appointment": "boolean", + "appointment_time": "YYYY-MM-DD HH:MM:SS" +} +``` + +You would get it filled out like this in your webhook once the call completes: + +```json +"analysis": { + "email_address": "johndoe@gmail.com", + "first_name": "John", + "last_name": "Doe", + "wants_to_book_appointment": true, + "appointment_time": "2024-01-01 12:00:00" +} +``` + + + + + Add any additional information you want to associate with the call. This can be useful for tracking or categorizing + calls. + + + + Set the pathway that your agent will follow. This will override the `prompt` field, so there is no need to pass the 'prompt' field if you are setting a pathway. + + Warning: Setting a pathway will set the following fields to `null` / their default value - `prompt`, `first_sentence`, `model`, `dynamic_data`, `tools` + + Set to `null` or an empty string to clear the pathway. + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```ENG``` - Spanish: ```ESP``` - French: + ```FRE``` - Polish: ```POL``` - German: ```GER``` - Italian: ```ITA``` - Brazilian Portuguese: ```PBR``` - Portuguese: + ```POR``` + + + + The webhook should be a http / https callback url. We will send the call_id + and transcript to this URL after the call completes. This can be useful if you + want to have real time notifications when calls finish. + +Set to `null` or an empty string to clear the webhook. + + + + + Select a model to use for your call. + +Options: `base`, `turbo` and `enhanced`. + +In nearly all cases, `enhanced` is the best choice for now. + + + +There are three different ways to use Bland: + +- `model: base` + + - The original, follows scripts/procedures most effectively. + - Supports all features and capabilities. + - Best for Custom Tools + +- `model: enhanced` + + - Much faster latency and very conversational, works best with objective-based prompts. + - Supports all features and capabilities. + +- `model: turbo` + + - The absolute fastest latency possible, can be verbose at times + - Limited capabilities currently (excludes Transferring, IVR navigation, Custom Tools) + - Extremely realistic conversation capabilities + + + + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + +To remove, set to `null` or an empty string. + + + + + Interact with the real world through API calls. + +Detailed tutorial here: [Custom Tools](/tutorials/custom-tools) + + + + + Integrate data from external APIs into your agent's knowledge. + +Set to `null` or an empty string to clear dynamic data settings. + +Detailed usage in the [Send Call](/api-v1/post/calls) endpoint. + + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `500` milliseconds. You'll encounter higher latency, but you'll be interrupted less frequently. + +Set to `null` to reset to default. + + + + + The maximum duration that calls to your agent can last before being automatically terminated. + +Set to `null` to reset to default. + + + +### Response + + + Whether the update was successful or not - will be `success` or `error`. + + + + A message describing the status of the update. + + + + An object containing the updated settings for the agent. + + + + If the update was unsuccessful, this will contain the settings that failed to update. Useful to determine how your + request is being interpreted on our end. + + + + +```json Response +{ + "status": "success", + "message": "Successfully updated agent 46f37229-7d12-44be-b343-6e68274cfbea.", + "updates": { + "model": "enhanced" + } +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents.mdx new file mode 100644 index 00000000000..22648ba0c44 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/agents.mdx @@ -0,0 +1,215 @@ +--- +title: "Create a Web Agent" +api: "POST https://api.bland.ai/v1/agents" +description: "Configure all of the settings for a new web agent." +--- + +### Headers + + + Your API key for authentication. + +Example web call usage (client side): + +```javascript +import { BlandWebClient } from "bland-client-js-sdk"; + +const agentId = "YOUR-AGENT-ID"; +const sessionToken = "YOUR-SESSION-TOKEN"; + +document.addEventListener("DOMContentLoaded", async () => { + document.getElementById("btn").addEventListener("click", async () => { + const blandClient = new BlandWebClient(agentId, sessionToken); + await blandClient.initConversation({ + sampleRate: 44100, + }); + }); +}); +``` + + +### Body + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + - Ends call when objective is complete or voicemail is detected + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + + + Set your agent's voice - all available voices can be found with the [List Voices](/api-v1/get/voices) endpoint. + + + + Define a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data. + +In the webhook response or whenever you retrieve call data later, you'll get the data you defined back under `analysis`. + +For example, if you wanted to retrieve this information from the call: + +```json +"analysis_schema": { + "email_address": "email", + "first_name": "string", + "last_name": "string", + "wants_to_book_appointment": "boolean", + "appointment_time": "YYYY-MM-DD HH:MM:SS" +} +``` + +You would get it filled out like this in your webhook once the call completes: + +```json +"analysis": { + "email_address": "johndoe@gmail.com", + "first_name": "John", + "last_name": "Doe", + "wants_to_book_appointment": true, + "appointment_time": "2024-01-01 12:00:00" +} +``` + + + + + Add any additional information you want to associate with the call. This can be useful for tracking or categorizing + calls. + + + + Set the pathway that your agent will follow. This will override the `prompt` field, so there is no need to pass the 'prompt' field if you are setting a pathway. + + Warning: Setting a pathway will set the following fields to `null` / their default value - `prompt`, `first_sentence`, `model`, `dynamic_data`, `tools` + + Set to `null` or an empty string to clear the pathway. + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```ENG``` - Spanish: ```ESP``` - French: + ```FRE``` - Polish: ```POL``` - German: ```GER``` - Italian: ```ITA``` - Brazilian Portuguese: ```PBR``` - Portuguese: + ```POR``` + + + + Select a model to use for your call. + +Options: `base`, `turbo` and `enhanced`. + +In nearly all cases, `enhanced` is the best choice for now. + + + +There are three different ways to use Bland: + +- `model: base` + + - The original, follows scripts/procedures most effectively. + - Supports all features and capabilities. + - Best for Custom Tools + +- `model: enhanced` + + - Much faster latency and very conversational, works best with objective-based prompts. + - Supports all features and capabilities. + +- `model: turbo` + + - The absolute fastest latency possible, can be verbose at times + - Limited capabilities currently (excludes Transferring, IVR navigation, Custom Tools) + - Extremely realistic conversation capabilities + + + + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + +To remove, set to `null` or an empty string. + + + + + Interact with the real world through API calls. + +Detailed tutorial here: [Custom Tools](/tutorials/custom-tools) + + + + + Integrate data from external APIs into your agent's knowledge. + +Set to `null` or an empty string to clear dynamic data settings. + +Detailed usage in the [Send Call](/api-v1/post/calls) endpoint. + + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `500` milliseconds. You'll encounter higher latency, but you'll be interrupted less frequently. + +Set to `null` to reset to default. + + + + + The maximum duration that calls to your agent can last before being automatically terminated. + +Set to `null` to reset to default. + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + +```json Response +{ + "agent": { + "agent_id": "2c565dc7-f41f-43db-a99f-e4c8ba9d7d18", + "webhook": null, + "dynamic_data": null, + "interruption_threshold": null, + "first_sentence": null, + "model": "enhanced", + "voice_settings": null, + "voice": "maya", + "prompt": "...", + "temperature": null, + "max_duration": 30, + "language": "ENG", + "tools": null + } +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches-id-analyze.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches-id-analyze.mdx new file mode 100644 index 00000000000..663762fc21e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches-id-analyze.mdx @@ -0,0 +1,93 @@ +--- +title: "Analyze Batch with AI" +api: "POST https://api.bland.ai/v1/batches/{batch_id}/analyze" +description: "Analyzes a batch of calls based on using questions and goals." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the batch of calls to be analyzed. + + +### Request Body + + + This is the overall purpose of the batch of calls. Provides context for the analysis to guide how the + questions/transcripts are interpreted. + + + + An array of questions to be analyzed for each call in the batch. + + Each question should be an array with two elements: the question text and the expected answer type (e.g., "string", "boolean"). + +Fairly flexible in terms of the expected answer type, and unanswerable questions will default to `null`. + +Examples: + +```json +"questions": [ + ["Who answered the call?", "human or voicemail"], + ["Positive feedback about the product: ", "string"], + ["Negative feedback about the product: ", "string"], + ["Customer confirmed they were satisfied", "boolean"] + ] +``` + + + +### Response + + + Will be `success` if the request was successful. + + + + Confirms the request was successful, or provides an error message if the request failed. + + + + Contains the analyzed answers for each call in the batch. + +The keys are `call_id`s from the batch, and the array values are the analysis results for each question in the batch. + + + + + Token-based price for the analysis request. + +As a rough estimate, the base cost is `0.003` credits with an additional `0.0015` credits per call in the batch. + +Longer call transcripts and higher numbers of questions can increase the cost, however the cost scales very effectively with batches vs. individual calls. + + + + +```json +{ + "status": "success", + "message": "Successfully analyzed batch", + "answers": { + "123e4567-e89b-12d3-a456-426614174000": [ + "human", + "Customer found the product sturdy and reliable", + "A bit heavy", + true + ], + "123e4567-e89b-12d3-a456-426614174001": [ + "voicemail", + null, + null, + false + ] + } +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches-id-stop.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches-id-stop.mdx new file mode 100644 index 00000000000..d2dbda1ea3b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches-id-stop.mdx @@ -0,0 +1,49 @@ +--- +title: "Stop Active Batch" +api: "POST https://api.bland.ai/v1/batches/{batch_id}/stop" +description: "Stops all active calls in a batch." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the batch to be cancelled. + + +### Response + + + The status of the request. If anything other than 'success', an error has occurred or all calls have already been + completed. + + + + A message describing the status of the request. + + + + The number of calls that were cancelled. + + + + The `batch_id` of the cancelled batch. + + + + +```json Response +{ + "status": "success", + "message": "Successfully stopped batch", + "num_calls": 1205, + "batch_id": "5e5b1a3a-2b0a-4b9e-8b9e-asdf-gen-batch" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches.mdx new file mode 100644 index 00000000000..df4e89a86a0 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/batches.mdx @@ -0,0 +1,91 @@ +--- +title: "Send a Batch of Calls" +api: "POST https://api.bland.ai/v1/batches" +description: "Send large volumes of calls at once with a single API request." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + This is the prompt or task used for all the phone calls in the request. Information can be inserted into it surrounding variable names with \{\{curly braces\}\}. + +Example: + +```json +"You are calling {{business}} to renew their subscription to {{service}} before it expires on {{date}}." +``` + + + + + Define a list of calls to make and their properties. Each call in call_data MUST have a "phone_number" property. Properties are case-sensitive. + +Example: + +```json +[ + { + "phone_number": "1234567890", + "business": "ABC Corp", + "service": "Netflix", + "date": "September 4th" + }, + { + "phone_number": "32176540987", + "business": "XYZ inc.", + "service": "Window Cleaning", + "date": "December 20th" + } +] +``` + + + + + Adds a user-friendly label to your batch to keep track of it's original intention. This can help differentiate + multiple call batches that are part of the same Campaign. Shown when a batch is retreived. + + + + Use ```campaign_id``` to organize related batches together. This can be set manually or auto-generated through + Campaigns. + + + + When this is set to ```true```, only the first call of ```call_data``` will be dispatched. A common use case is to set the first ```phone_number``` value to your own to confirm everything's set up properly. + +Includes additional information in the response when true so that it's easier to find any issues. + + + + + All other parameters supported by the [Send Call](/api-v1/post/calls) endpoint are supported here as well. They will + be applied to each call in the batch. + + +### Response + + + If anything other than "success" is returned, there was an error. + + + + The unique identifier for the batch. + + + + +```json Response +{ + "message": "success", + "batch_id": "3p$7rQ3p9sT5bzmF-gen-batch" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-id-analyze.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-id-analyze.mdx new file mode 100644 index 00000000000..f8be1374190 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-id-analyze.mdx @@ -0,0 +1,82 @@ +--- +title: "Analyze Call with AI" +api: "POST https://api.bland.ai/v1/calls/{call_id}/analyze" +description: "Analyzes a call of calls based using questions and goals." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the call to be analyzed. + + +### Request Body + + + This is the overall purpose of the call. Provides context for the analysis to guide how the questions/transcripts are + interpreted. + + + + An array of questions to be analyzed for the call. + + Each question should be an array with two elements: the question text and the expected answer type (e.g., "string", "boolean"). + +Fairly flexible in terms of the expected answer type, and unanswerable questions will default to `null`. + +Examples: + +```json +"questions": [ + ["Who answered the call?", "human or voicemail"], + ["Positive feedback about the product: ", "string"], + ["Negative feedback about the product: ", "string"], + ["Customer confirmed they were satisfied", "boolean"] + ] +``` + + + +### Response + + + Will be `success` if the request was successful. + + + + Confirms the request was successful, or provides an error message if the request failed. + + + + Contains the analyzed answers for the call in an array. + + + + Token-based price for the analysis request. + +As a rough estimate, the base cost is `0.003` credits with an additional `0.0015` credits per call in the call. + +Longer call transcripts and higher numbers of questions can increase the cost, however the cost scales very effectively with calls vs. individual calls. + + + + +```json +{ + "status": "success", + "message": "Successfully analyzed call", + "answers": [ + "human", + "Customer found the product sturdy and reliable", + "A bit heavy", + true + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-id-stop.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-id-stop.mdx new file mode 100644 index 00000000000..acacaa381e9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-id-stop.mdx @@ -0,0 +1,37 @@ +--- +title: "Stop Active Call" +api: "POST https://api.bland.ai/v1/calls/{call_id}/stop" +description: "End an active phone call by call_id." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the call you want to end. + + +### Response + + + Can be `success` or `error`. + + + + If the status is `success`, the message will say "Call ended successfully." Otherwise, if the status is `error`, the + message will say "SID not found for the given c_id." or "Internal server error." + + + +```json +{ + "status": "success", + "message": "Call ended successfully." +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-simple-pathway.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-simple-pathway.mdx new file mode 100644 index 00000000000..25b29bc89a1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-simple-pathway.mdx @@ -0,0 +1,75 @@ +--- +title: "Send Call using Pathways (Simple)" +api: "POST https://api.bland.ai/v1/calls" +description: "Send an AI phone call with your own conversational pathway agent! +Links - [Video Tutorial](https://www.loom.com/share/5ce5a84ec97149efad7cf5eff66a93c5?sid=697dc436-53cf-494c-a3e9-a25031df6496) | [Step-by-step web tutorial](https://docs.bland.ai/tutorials/pathways)" +--- + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to call. Country code defaults to `+1` (US) if not specified. + + Formatting is flexible, however for the most predictable results use the [E.164](https://www.twilio.com/docs/glossary/what-e164#examples-of-e164-numbers) format. + + + Expected/Ideal Format: + - "+12223334444" + - "+91223334444" + - "+61223334444" + + Valid, but not recommended: + - "2223334444" + - "+1 (222) 333-4444" + - "+1 222 333 4444" + - "222-333-4444" + + Invalid: + - "12223334444" + - "552223334444" + - "non-numeric characters" + - "2223334444 ext. 123" + + + + + + Follows the conversational pathway you created to guide the conversation. + +You can access your pathway_id by clicking on the 'Copy ID' button on your pathways [here](https://app.bland.ai/home?page=convo-pathways). If you don't have any pathways, click the 'Create Pathway' button to create one! + + + + [Video tutorial](https://www.loom.com/share/5ce5a84ec97149efad7cf5eff66a93c5?sid=697dc436-53cf-494c-a3e9-a25031df6496) + + [Step by step Web Tutorial](https://docs.bland.ai/tutorials/pathways) + + + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-simple.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-simple.mdx new file mode 100644 index 00000000000..ea24eaba499 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls-simple.mdx @@ -0,0 +1,82 @@ +--- +title: "Send Call (Simple)" +api: "POST https://api.bland.ai/v1/calls" +description: "Send an AI phone call with a custom objective and actions." +--- + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to call. Country code defaults to `+1` (US) if not specified. + + Formatting is flexible, however for the most predictable results use the [E.164](https://www.twilio.com/docs/glossary/what-e164#examples-of-e164-numbers) format. + + + Expected/Ideal Format: + - "+12223334444" + - "+91223334444" + - "+61223334444" + + Valid, but not recommended: + - "2223334444" + - "+1 (222) 333-4444" + - "+1 222 333 4444" + - "222-333-4444" + + Invalid: + - "12223334444" + - "552223334444" + - "non-numeric characters" + - "2223334444 ext. 123" + + + + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + - Ends call when objective is complete or voicemail is detected + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls.mdx new file mode 100644 index 00000000000..186acf39e65 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/calls.mdx @@ -0,0 +1,549 @@ +--- +title: "Send Call" +api: "POST https://api.bland.ai/v1/calls" +description: "Send an AI phone call with a custom objective and actions." +--- + +### Headers + + + Your API key for authentication. + + + + A special key for using a BYOT (Bring Your Own Twilio) account. Not required in most cases. + + +### Body + + + The phone number to call. Country code defaults to `+1` (US) if not specified. + + Formatting is flexible, however for the most predictable results use the [E.164](https://www.twilio.com/docs/glossary/what-e164#examples-of-e164-numbers) format. + + + Expected/Ideal Format: + - "+12223334444" + - "+91223334444" + - "+61223334444" + + Valid, but not recommended: + - "2223334444" + - "+1 (222) 333-4444" + - "+1 222 333 4444" + - "222-333-4444" + + Invalid: + - "12223334444" + - "552223334444" + - "non-numeric characters" + - "2223334444 ext. 123" + + + + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + - Ends call when objective is complete or voicemail is detected + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + + + The voice of the AI agent to use. Accepts any form of voice ID, including custom voice clones and voice presets. + +Default voices can be referenced directly by their name instead of an id. + +Usage example: `voice: "maya"` + +Bland Curated voices: + +- `maya` +- `mason` +- `ryan` +- `adriana` +- `tina` +- `matt` +- `evelyn` + +Use the [GET /v1/voices](https://api.bland.ai/voices) endpoint to see a full list of your available voices. + + + + Note: Including `voice_id` or `reduce_latency` in your request is still supported, but not recommended. + + The previous structure to select voices used both `voice_id` and `reduce_latency`. To simplify the process, we've combined these into a single `voice` parameter. + + - If the first two letters of `voice` are `RL`, that is equivalent to settings `reduce_latency` to `true`. + - Prefixing the voice ID with `HQ` will use the high fidelity version of the voice. + - The integer following the prefix is the `voice_id` from before. + + Example: + - `reduce_latency: true, voice_id: 0` is equivalent to `voice: "RL0"` + - `reduce_latency: false, voice_id: 3` is equivalent to `voice: "HQ3"` + + Including `reduce_latency` may override the `voice` parameter, so exclude it when using `voice`. + + + + + All presets have been migrated to the `voice` parameter and can use either the preset name or ID. + + If you used to have a `voice_preset_id` of `"2f9fdbc7-4bf2-4792-8a18-21ce3c93978f"`, you can now use `voice: "2f9fdbc7-4bf2-4792-8a18-21ce3c93978f"`. + + + + + + Define a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data. + +In the webhook response or whenever you retrieve call data later, you'll get the data you defined back under `analysis`. + +For example, if you wanted to retrieve this information from the call: + +```json +"analysis_schema": { + "email_address": "email", + "first_name": "string", + "last_name": "string", + "wants_to_book_appointment": "boolean", + "appointment_time": "YYYY-MM-DD HH:MM:SS" +} +``` + +You would get it filled out like this in your webhook once the call completes: + +```json +"analysis": { + "email_address": "johndoe@gmail.com", + "first_name": "John", + "last_name": "Doe", + "wants_to_book_appointment": true, + "appointment_time": "2024-01-01 12:00:00" +} +``` + + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without + `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + + + + Should the AI speak first or wait for someone else to talk? + + Creates more realistic conversations when answered with "Hello?", "This is \{name\} speaking." and so on. + + - When ```false```: The AI starts speaking shortly after the call is answered. + + - When ```true```: The AI will wait for the answerer to speak. + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `200` milliseconds. You'll encounter higher latency, but you'll be interrupted much less frequently. + + + + + This is the pathway ID for the pathway you have created on our dev portal. You + can access the ID of your pathways by clicking the 'Copy ID' button of your + pathway [here](https://app.bland.ai/home?page=convo-pathways) + +Note: Certain parameters do not apply when using pathways. + +{" "} + + + - `task` - The pathway substitutes as the agent's instructions. - `model` - We use our own fine-tuned models under the + hood. - `tools` - Replaced by 'Webhook' Node in Pathways - `transfer_list` - Replaced by 'Transfer Call' Node in + Pathways - `transfer_phone_number` - Replaced by 'Transfer Call' Node in Pathways + + +Example Simple Request body: + +```json +"phone_number": "+1975934749", +"pathway_id": "a0f0d4ed-f5f5-4f16-b3f9-22166594d7a7" +``` + + + + + Select a model to use for your call. + +Options: `gpt4`, `base`, `turbo` and `enhanced`. + +In nearly all cases, `enhanced` is the best choice for now. + + + +There are four different ways to use Bland: + +- `model: gpt4` + + - Slow but accurate + - Supports all features and capabilities. + - Best for complex tasks where latency isn't a priority + +- `model: base` + + - The original, follows scripts/procedures most effectively. + - Supports all features and capabilities. + - Best for Custom Tools + +- `model: enhanced` + + - Much faster latency and very conversational, works best with objective-based prompts. + - Supports all features and capabilities. + +- `model: turbo` + + - The absolute fastest latency possible, can be verbose at times + - Limited capabilities currently (excludes Transferring, IVR navigation, Custom Tools) + - Extremely realistic conversation capabilities + + + + + + + Interact with the real world through API calls. + +Detailed tutorial here: [Custom Tools](/tutorials/custom-tools) + + + + + Add any additional information you want to associate with the call. This can be useful for tracking or categorizing calls. + +Anything that you put here will be returned in your webhook or in the call details under `metadata`. + +Example: + +```json +"metadata": { + "campaign_id": "1234", + "source": "web" +} +``` + + + + + The webhook should be a http / https callback url. We will send the call_id and transcript to this URL after the call + completes. This can be useful if you want to have real time notifications when calls finish. + + + + To record your phone call, set `record` to true. When your call completes, you can access through the `recording_url` + field in the call details or your webhook. + + + + A phone number that the agent can transfer to under specific conditions - such as being asked to speak to a human or supervisor. + + + For best results: + - Specify conditions that the agent should transfer to a human under (examples are great!) + - In the `task`, refer to the action solely as "transfer" or "transferring". + - Alternate phrasing such as "swap" or "switch" can mislead the agent, causing the action to be ignored. + + + + + + Give your agent the ability to transfer calls to a set of phone numbers. + +Overrides `transfer_phone_number` if a `transfer_list.default` is specified. + +Will default to `transfer_list.default`, or the chosen phone number. + +Example usage to route calls to different departments: + +```json +"transfer_list": { + "default": "+12223334444", + "sales": "+12223334444", + "support": "+12223334444", + "billing": "+12223334444" +} +``` + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```ENG``` - Spanish: ```ESP``` - French: + ```FRE``` - Polish: ```POL``` - German: ```GER``` - Italian: ```ITA``` - Brazilian Portuguese: ```PBR``` - Portuguese: + ```POR``` + + + + Set the longest you want the call to possibly go in minutes. After the max_duration minutes have passed, the call will + automatically end. Example Values: ```20, 2``` + + + + Enables machine detection when the call starts to determine whether the call was answered by a person or a voicemail. + +Best Practices (when enabled): + +- Since the determination is made at the beginning of the call, use `wait_for_greeting` to try and coax a human response. +- If combined with `first_sentence`, try wording it so the person answering says something back - ex. `"Hello?"` or `"Is this \{\{name\}\}?"`. + +Price: `$0.02` per call, however there is no charge for unanswered calls or calls that failed to send. + + + + + Specify a purchased Outbound Number to call from. Country code is required, spaces or parentheses must be excluded. + +By default, calls are initiated from a separate pool of numbers owned by Bland. + + + + + The pronunciation guide is an `array` of `objects` that guides the LLM on how to say specific words. This is great for situations with complicated terms or names. + +````json + [ + { + "word": "example", + "pronunciation": "ex-am-ple", + "case_sensitive": "false", + "spaced": "false" + }, + { + "word": "API", + "pronunciation": "A P I", + "case_sensitive": "true", + "spaced": "true" + } + ] + ``` + + + + - `word` + — the word you want to guide the LLM on how to pronounce + - `pronunciation` + — the word you want to guide the LLM on how to pronounce. + - `case_sensitive` + — whether or not to consider case. Particularly useful with names. EG: 'Max' the name versus 'max' the word. Defaults to false. `Not required`. + - `spaced` + — whether or not to consider spaces. When `true` the word 'high' would be flagged but NOT 'hightop'. Defaults to true. `Not required`. + + + + + + A value between 0 and 1 that controls the randomness of the LLM. 0 will cause more deterministic outputs while 1 will cause more random. + + Example Values: ```"0.9", "0.3", "0.5"``` + + + + The time you want the call to start. If you don't specify a time (or the time is in the past), the call will send immediately. + + Set your time in the format `YYYY-MM-DD HH:MM:SS -HH:MM` (ex. `2021-01-01 12:00:00 -05:00`). + + The timezone is optional, and defaults to UTC if not specified. + + Note: Scheduled calls can be cancelled with the [POST /v1/calls/:call_id/stop](/api-v1/post/calls-id-stop) endpoint. + + + + When the AI encounters a voicemail, it will leave this message after the beep and then immediately end the call. + + Warning: If `amd` is set to `true` or `voicemail_action` is set to `ignore`, then this will still work for voicemails, but it will not hang up for IVR systems. + + + + This is processed separately from the AI's decision making, and overrides it. + + Options: + - ```hangup``` + - ```leave_message ``` + - ```ignore``` + + Examples: + - Call is answered by a voicemail (specifically with a beep or tone): + - If `voicemail_message` is set, that message will be left and then the call will end. + - Otherwise, the call immediately ends (regardless of `amd`) + + - Call is answered by an IVR system or phone tree: + - If `amd` is set to `true`, the AI will navigate the system and continue as normal. + - If `voicemail_action` is set to `ignore`, the AI will ignore the IVR and continue as normal. + - Otherwise, if `voicemail_message` is set then it'll leave that message and end the call. + - Finally, if none of those conditions are met, the call will end immediately. + + Note: If `voicemail_message` is set, then the AI will leave the message regardless of the `voicemail_action`. + + + + AMD mode helps your AI navigate phone trees and IVR systems. If you know your call will hit an automated system you should switch it on. + + Behavioral changes: + - Much higher `interruption_threshold` so that the options are listened to in full. + - Underlying prompt is adjusted so the AI is aware it's navigating a phone tree. + + NOTE: AMD mode causes increased delay for the first response, even if answered by a human. Highly recommended to set to `false` in the majority of cases. + + + + When you want your AI to "know" a specific fact - like the caller's + name or other relevant context. + + The AI agent will be aware of both the key names as well as their corresponding values. + + + Example Issue: + - The LLM is hallucinating specific facts. You need to provide specific information. + Example Solution: + - Use `request_data` to specify and label that data. + + ```json + "request_data": { + "first_name":"John", + "date_of_birth":"03/14/05" + // additional parameters as needed + } + ``` + + + + + Make dynamic requests to external APIs and use the data in your AI's responses. + + + Each request object should contain: + + `url`: The URL of the external API to fetch data from. + + `response_data`: An array of objects describing how to parse and use the data fetched from the API. Explained in more detail below. + + The following are optional: + + `method`: Allows `GET` or `POST`. Default: `GET` + + `cache`: Whether to fetch the data once at the beginning of the call, or to re-check continuously for data that might change mid-call. Default: `true` + + `headers`: An object of headers to send with the request. + + `body`: The body of the request. + + The following variables can be injected into the dynamic request body: + + - `{{from}}` (Ex. `+12223334444`) + - `{{to}}` + - `{{short_from}}` (Ex. `2223334444`) + - `{{short_to}}` + - `{{call_id}}` + + These string values will be replaced in each `dynamic_data[].body` where they're used by system values in each request. + + Try out with this example: +```json + "dynamic_data": [ + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json", + "response_data": [ + { + "name": "BTC Price USD", + "data": "bpi.USD.rate", + "context": "Current BTC Price: ${{BTC Price USD}} USD" + }, + { + "name": "BTC Price EUR", + "data": "bpi.EUR.rate", + "context": "In Euros: {{BTC Price USD}} EUR" + } + ] + } + ] +```` + + + An array of objects describing how to parse and use the data fetched from the API. + + Each object in this array should contain: + - `name`: A label for the fetched data. + - Example: `"Flight Status"` + - `data`: The JSON path in the API response to extract the data from. + - Example: `"user.flights[0].status"` + - `context`: How this data should be incorporated into the AI's knowledge. + - Example: `"John's flight is currently {{Flight Status}}"` + + + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + The batch ID of the call (present only if status is `success`). + + + + A message explaining the status of the call. + + + + For validation errors, a detailed list of each field with an error and it's error message. + +Example: + +```json +{ + "status": "error", + "message": "Invalid parameters", + "errors": [ + "Missing required parameter: phone_number.", + "Missing required parameter: task.", + "Phone number must be a string or number.", + "Task must be a string." + ] +} +``` + + + + + +```json Response +{ + "status": "success", + "message": "Call successfully queued.", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1", + "batch_id": null +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-insert.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-insert.mdx new file mode 100644 index 00000000000..d52f27977cc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-insert.mdx @@ -0,0 +1,56 @@ +--- +title: "Upload Inbound Phone Numbers" +api: "POST https://api.bland.ai/v1/inbound/insert" +description: "Add inbound numbers to Bland from your own Twilio account. See [Enterprise Twilio Integration](/enterprise-features/custom-twilio) for more information." +--- + +### Headers + + + Your API key for authentication. + + + + The `encrypted_key` of the Twilio account you want to upload numbers from. + + +### Body + + + An array of phone numbers you want to upload to Bland. + + Include the leading `'+'`, country code and the phone number without any special characters. + + Example: `["+12223334444", "+13334445555"]` + + + +### Response + + + Can be `success` or `error`. + + + + A message saying whether the insertion succeeded, or a helpful message describing why it failed. + + + + An array of phone numbers that were successfully inserted. + +Any phone numbers that failed to be inserted will not be included in this array - for example if they are already in your account or not associated with the sepcified Twilio account. + + + + +```json +{ + "status": "success", + "message": "Successfully inserted numbers", + "inserted": [ + "+12223334444", + "+13334445555" + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-number-delete.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-number-delete.mdx new file mode 100644 index 00000000000..484df899182 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-number-delete.mdx @@ -0,0 +1,40 @@ +--- +title: "Delete Inbound Phone Number" +api: "POST https://api.bland.ai/v1/inbound/{phone_number}/delete" +description: "Remove an inbound number that was uploaded through your own Twilio account. See [Enterprise Twilio Integration](/enterprise-features/custom-twilio) for more information." +--- + +### Headers + + + Your API key for authentication. + + + + The `encrypted_key` for the Twilio account that owns the phone number you want to delete. + + +### Path + + + The phone number you want to remove from Bland's system. + + +### Response + + + Can be `success` or `error`. + + + + A message saying whether the deletion succeeded, or a helpful message describing why it failed. + + + +```json +{ + "status": "success", + "message": "Successfully deleted number from database: +15555555555" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-number-update.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-number-update.mdx new file mode 100644 index 00000000000..cdec91a5e9f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-number-update.mdx @@ -0,0 +1,273 @@ +--- +title: "Update Inbound Details" +api: "POST https://api.bland.ai/v1/inbound/{phone_number}" +description: "Update your inbound agent's settings, prompt and other details." +--- + +### Headers + + + Your API key for authentication. + + + + The `encrypted_key` for the Twilio account that owns the phone number you want to modify. Not required if you are + using a Bland phone number. + + +### Path Parameters + + + The inbound phone number you wish to update. + + Formatting Notes: + - The `'+'` or `'%2B'` prefix is optional. + - Will assume a US country code if no country code is provided. + + Valid Examples for `+13334445555`: + - `%2B13334445555` + - `13334445555` + - `3334445555` + + + +### Body + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + For inbound numbers, consider including additional context about the purpose of the call, and what types of callers to expect. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + + + Set your agent's voice - all available voices can be found with the [List Voices](/api-v1/get/voices) endpoint. + + + + Define a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data. + +In the webhook response or whenever you retrieve call data later, you'll get the data you defined back under `analysis`. + +For example, if you wanted to retrieve this information from the call: + +```json +"analysis_schema": { + "email_address": "email", + "first_name": "string", + "last_name": "string", + "wants_to_book_appointment": "boolean", + "appointment_time": "YYYY-MM-DD HH:MM:SS" +} +``` + +You would get it filled out like this in your webhook once the call completes: + +```json +"analysis": { + "email_address": "johndoe@gmail.com", + "first_name": "John", + "last_name": "Doe", + "wants_to_book_appointment": true, + "appointment_time": "2024-01-01 12:00:00" +} +``` + + + + + Add any additional information you want to associate with the call. This can be useful for tracking or categorizing + calls. + + + + Set the pathway that your agent will follow. This will override the `prompt` field, so there is no need to pass the 'prompt' field if you are setting a pathway. + + Warning: Setting a pathway will set the following fields to `null` / their default value - `prompt`, `first_sentence`, `model`, `dynamic_data`, `tools` + + Set to `null` or an empty string to clear the pathway. + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```ENG``` - Spanish: ```ESP``` - French: + ```FRE``` - Polish: ```POL``` - German: ```GER``` - Italian: ```ITA``` - Brazilian Portuguese: ```PBR``` - Portuguese: + ```POR``` + + + + The webhook should be a http / https callback url. We will send the call_id + and transcript to this URL after the call completes. This can be useful if you + want to have real time notifications when calls finish. + +Set to `null` or an empty string to clear the webhook. + + + + + Give your agent the ability to transfer calls to a set of phone numbers. + +Overrides `transfer_phone_number` if a `transfer_list.default` is specified. + +Will default to `transfer_list.default`, or the chosen phone number. + +Example usage to route calls to different departments: + +```json +"transfer_list": { + "default": "+12223334444", + "sales": "+12223334444", + "support": "+12223334444", + "billing": "+12223334444" +} +``` + + + + + Select a model to use for your call. + +Options: `base`, `turbo` and `enhanced`. + +In nearly all cases, `enhanced` is the best choice for now. + + + +There are three different ways to use Bland: + +- `model: base` + + - The original, follows scripts/procedures most effectively. + - Supports all features and capabilities. + - Best for Custom Tools + +- `model: enhanced` + + - Much faster latency and very conversational, works best with objective-based prompts. + - Supports all features and capabilities. + +- `model: turbo` + + - The absolute fastest latency possible, can be verbose at times + - Limited capabilities currently (excludes Transferring, IVR navigation, Custom Tools) + - Extremely realistic conversation capabilities + + + + + + + A phone number that the agent can transfer to under specific conditions - such as being asked to speak to a human or supervisor. + +Set to `null` to remove. + + + For best results: + - Specify conditions that the agent should transfer to a human under (examples are great!) + - In the `task`, refer to the action solely as "transfer" or "transferring". + - Alternate phrasing such as "swap" or "switch" can mislead the agent, causing the action to be ignored. + + + + + + To record your phone call, set `record` to true. When your call completes, you can access through the `recording_url` + field in the call details or your webhook. + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + +To remove, set to `null` or an empty string. + + + + + Interact with the real world through API calls. + +Detailed tutorial here: [Custom Tools](/tutorials/custom-tools) + + + + + Integrate data from external APIs into your agent's knowledge. + +Set to `null` or an empty string to clear dynamic data settings. + +Detailed usage in the [Send Call](/api-v1/post/calls) endpoint. + + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `500` milliseconds. You'll encounter higher latency, but you'll be interrupted less frequently. + +Set to `null` to reset to default. + + + + + The maximum duration that calls to your agent can last before being automatically terminated. + +Set to `null` to reset to default. + + + +### Response + + + Whether the update was successful or not - will be `success` or `error`. + + + + A message describing the status of the update. + + + + An object containing the updated settings for the inbound number. + + + + If the update was unsuccessful, this will contain the settings that failed to update. Useful to determine how your + request is being interpreted on our end. + + + + +```json Response +{ + "status": "success", + "message": "Successfully updated number +18584139939.", + "updates": { + "prompt": "(Your prompt)", + "voice": "maya", + "webhook": null, + "first_sentence": "Roberta speaking, how can I help you?", + "record": false, + "max_duration": 30, + "model": "enhanced" + //... + } +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-purchase.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-purchase.mdx new file mode 100644 index 00000000000..8173056ca6c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/inbound-purchase.mdx @@ -0,0 +1,61 @@ +--- +title: "Purchase Inbound number" +api: "POST https://api.bland.ai/v1/inbound/purchase" +description: "Purchase and configure a new inbound phone number. ($15/mo. subscription using your stored payment method)." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + Choose a three-digit area code for your phone number. If set as a parameter, a number will only be purchased by exact + match if available. + + + + This defines how the AI will start the conversation, information available to it, and its behaviors. Matches how the + outbound `task` parameter functions. + + + + Choose a country code for your phone number. + +Options: `"US"` or `"CA"` for Canada. For others, please contact support. + + + + + The webhook should be a http / https callback url. We will send the call_id and transcript to this URL after the call + completes. This can be useful if you want to have real time notifications when calls finish. + + + + Specify an exact phone number you'd like to use. If provided, will override the `area_code` parameter and does not fall back to any other number. + +Example of the correct format (Note the `"+1"` is mandatory): `"+12223334444"` + + + +### Response + + + The created phone number, will be in the following format: `+1XXXXXXXXXX` + +Example: `+18582814611` + + + + + +```json Response +{ + "phone_number": "+18582814611" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/outbound-purchase.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/outbound-purchase.mdx new file mode 100644 index 00000000000..d0326c09240 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/outbound-purchase.mdx @@ -0,0 +1,37 @@ +--- +title: "Purchase Outbound number" +api: "POST https://api.bland.ai/v1/outbound/purchase" +description: "Purchase and configure a new inbound phone number. ($15/mo. subscription using your stored payment method)." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + Choose a three-digit area code for your phone number. If set as a parameter, a number will only be purchased by exact + match if available. + + +### Response + + + The created phone number, will be in the following format: `+1XXXXXXXXXX` + +Example: `+18582814611` + + + + + +```json Response +{ + "phone_number": "+18582814611" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-analyze.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-analyze.mdx new file mode 100644 index 00000000000..e0597fd5d1d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-analyze.mdx @@ -0,0 +1,52 @@ +--- +title: "SMS Conversation Analysis" +api: "POST https://api.bland.ai/v1/sms/analyze" +description: "Answer questions and extract information from an SMS conversation." +--- + +### Headers + + + Your API key for authentication. + +### Body + + + An overarching goal for the information you want to extract from the SMS messages. + + + +An array of questions that you want the AI to answer, along with their return types. + +For example: + +```json +{ + "answers": [ + ["When does Bob want to move?", "time"], + ["Summarize the call.", "summary"] + ] +} +``` + + + + + The phone number that received the messages. + + + + The human/other phone number in the conversation. + + + + +```json Response +{ + "status": "success", + "message": "Successfully analyzed SMS messages.", + "answers": ["Bob prefers to have movers come in the morning.", "..."] +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-check-registration.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-check-registration.mdx new file mode 100644 index 00000000000..82f037fb74b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-check-registration.mdx @@ -0,0 +1,29 @@ +--- +title: "Check SMS A2P status" +api: "POST https://api.bland.ai/v1/sms/register/status" +description: "Check the status of an A2P registration." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + The `registration_id` for the a2p registration. + + + + +```json Response +{ + status: "pending" || "approved" || "failed", + brandType: //string of the brand type, + failureReason: null || "reason here" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-get-messages.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-get-messages.mdx new file mode 100644 index 00000000000..631390cf779 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-get-messages.mdx @@ -0,0 +1,33 @@ +--- +title: "Get SMS Messages" +api: "POST https://api.bland.ai/v1/sms/messages/get" +description: "Get the list of SMS messages for a given conversation." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + +The `to` number in the conversation. This is the number you *do not* own. + + +The `from` number in the conversation. This is the number you *do* own. + +**Please note any ordering of numbers will work** + + + + + +```json Response +{ + "messages": "[]" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-prompt-update.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-prompt-update.mdx new file mode 100644 index 00000000000..f92ee107246 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-prompt-update.mdx @@ -0,0 +1,34 @@ +--- +title: "Update SMS Prompt" +api: "POST https://api.bland.ai/v1/sms/update" +description: "Update your SMS agent's prompt." +--- + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to update. + + + + The prompt for the AI to use when replying. + + + + Pass in an array of strings, that if present, the AI should not respond to. Set to `null` to disable. + + + + +```json Response +{ + "status": "Prompt updated" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-submit-reg.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-submit-reg.mdx new file mode 100644 index 00000000000..90492fe4be2 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-submit-reg.mdx @@ -0,0 +1,68 @@ +--- +title: "A2P Registration" +api: "POST https://api.bland.ai/v1/sms/register" +description: "Learn how to register your A2P brand using our API. This guide covers all you need to know about making a POST request, including required parameters, error handling, and response expectations." +--- + +# Registering an A2P Brand via API + +This documentation provides detailed information on how to register an Application-to-Person (A2P) brand by making a POST request to our API. The process involves submitting your brand's details for registration and verification purposes. + +A2P Registration is required for _all_ businesses who wish to send SMS. There can be signifcant fines for any non compliant messages. A2P Registration can take 2 days -> 2 Weeks. + +## Endpoint + +`POST /api/registerA2PBrand` + +## Required Headers + +- `Authorization`: Your API key for authentication. + +## Request Parameters + +Your request should include a JSON body with the following parameters: + +- `businessName` (string): The legal name of your business. +- `ein` (string): Your Employer Identification Number. +- `vertical` (string): Industry vertical. Possible values include: "AUTOMOTIVE", "AGRICULTURE", "BANKING", "CONSTRUCTION", "CONSUMER", "EDUCATION", "ENGINEERING", "ENERGY", "OIL_AND_GAS", "FAST_MOVING_CONSUMER_GOODS", "FINANCIAL", "FINTECH", "FOOD_AND_BEVERAGE", "GOVERNMENT", "HEALTHCARE", "HOSPITALITY", "INSURANCE", "LEGAL", "MANUFACTURING", "MEDIA", "ONLINE", "PROFESSIONAL_SERVICES", "RAW_MATERIALS", "REAL_ESTATE", "RELIGION", "RETAIL", "JEWELRY", "TECHNOLOGY", "TELECOMMUNICATIONS", "TRANSPORTATION", "TRAVEL", "ELECTRONICS", "NOT_FOR_PROFIT" +- `address` (string): The business address. +- `city` (string): The city of your business. +- `state` (string): The state of your business. Must be a valid US state code. +- `postalCode` (string): The postal code of your business. +- `country` (string): The country of your business. +- `email` (string): The email address for your business. +- `type` (string): Legal structure of the business. Possible values: "Partnership", "Limited Liability Corporation", "Co-operative", "Non-profit Corporation", "Corporation" +- `website` (string): Your business's website URL. +- `opt_in_info` (string): Information regarding opt-in procedures for your messaging service. EX: "Customers must explicitly consent on our website and during the phone call." +- `messageSamples` (array): An array of three strings, each a sample message you plan to use. +- `trusted_user` (object): An object containing details about the trusted user registering the brand. Includes `position`, `last_name`, `phone_number`, `first_name`, and `email`. + +Ex. of trusted_user obj: + +```json +{ + "position": "CEO" //must be C Suite or VP, + "last_name":"Smith", + "first_name":"John" +} +``` + +## Error Handling + +Our API provides detailed error messages to help you understand what went wrong in case of a failure: + +- `400 Bad Request`: This response occurs if any required fields are missing in your request or if the state code is invalid. The response body will include a message specifying the missing or incorrect fields. +- `500 Internal Server Error`: Indicates an unexpected error on the server side. The response body will contain an error message with more details. + +## Successful Response + +A successful request returns a `200 OK` status code with a JSON body containing a message indicating the registration was successful and any relevant data or identifiers related to the A2P brand registration. + +**\*Important\*\*** The Brand Registration can take several attempts and days to weeks to complete. This success only indicates we _submitted_ the registration correctly. + +```json +{ + "message": "A2P Brand registration successful.", + "data": {...} +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-toggle-human.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-toggle-human.mdx new file mode 100644 index 00000000000..29ffde4b9e7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-toggle-human.mdx @@ -0,0 +1,35 @@ +--- +title: "Toggle SMS Reply Method" +api: "POST https://api.bland.ai/v1/sms/toggle" +description: "Turn on or off the AI replying for a given phone number." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + The phone number to update. + + + +Turn human mode on or off. + +`true` means that the AI will _not_ reply. +`false` means the AI will reply + + + + + +```json Response +{ + "status": "Turned human mode on" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-webhook-update.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-webhook-update.mdx new file mode 100644 index 00000000000..ab26a1e2e26 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/sms-webhook-update.mdx @@ -0,0 +1,30 @@ +--- +title: "Update SMS Webhook" +api: "POST https://api.bland.ai/v1/sms/webhook/update" +description: "Update the webhook for a given phone number." +--- + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to update. + + + + The webhook to fire when an SMS is received. + + + + +```json Response +{ + "status": "success" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-disable.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-disable.mdx new file mode 100644 index 00000000000..a45ce161544 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-disable.mdx @@ -0,0 +1,46 @@ +--- +title: "Disable Subaccount" +api: "POST https://api.bland.ai/v1/subaccounts/{subaccount_id}/disable" +description: "Immediately disables a subaccount and transfers any remaining credits back to the parent account." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the subaccount to which you want to transfer credits. + + +### Response + + + Whether the subaccount was successfully disabled. + + + + The affected subaccount's unique identifier. + + + + The amount of credits transferred back to the parent account. + + + + The new balance of the parent account after the transfer. + + + +```json +{ + "status": "success", + "subaccount_id": "1234567890", + "transferred_amount": 1000, + "new_parent_balance": 5000 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-rotate.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-rotate.mdx new file mode 100644 index 00000000000..a8c848294f7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-rotate.mdx @@ -0,0 +1,41 @@ +--- +title: "Rotate Subaccount API Key" +api: "POST https://api.bland.ai/v1/subaccounts/{subaccount_id}/rotate" +description: "Replace the API key of a subaccount with a new one." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the subaccount to which you want to transfer credits. + + +### Response + + + Whether the subaccount creation succeeded. + + + + The affected subaccount's unique identifier. + + + + The new API key for the subaccount. + + + +```json +{ + "status": "success", + "subaccount_id": "1234567890", + "api_key": "sub-sk-1234567890" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-transfer.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-transfer.mdx new file mode 100644 index 00000000000..aaadf3f1534 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts-id-transfer.mdx @@ -0,0 +1,50 @@ +--- +title: "Transfer Credit to a Subaccount" +api: "POST https://api.bland.ai/v1/subaccounts/{subaccount_id}/transfer" +description: "Transfer API credits from your account to a subaccount." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the subaccount to which you want to transfer credits. + + +### Body + + + This many of your API credits will be transferred to the subaccount. + + The balance must be a positive integer that is at least 1, and less than your current account balance. + + + +### Response + + + Whether the subaccount creation succeeded. + + + + The new balance of your account after the transfer. + + + + The new balance of the subaccount after the transfer. + + + +```json +{ + "status": "success", + "new_parent_balance": 1200, + "new_subaccount_balance": 500 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts.mdx new file mode 100644 index 00000000000..bd9010123eb --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/subaccounts.mdx @@ -0,0 +1,70 @@ +--- +title: "Create Subaccount" +api: "POST https://api.bland.ai/v1/subaccounts" +description: "Provision a new subaccount with separate billing, access and usage." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + Moves a portion of your API credit balance to the newly created subaccount. + + The balance must be a positive integer that is at least 10, and less than your current account balance. + + Special per-minute pricing plans carry over automatically to the subaccount, while increased rate limits do not. + + Unused credits can be reclaimed later with the subaccount's balance if needed. + + + + + The first name of the user who will be using the subaccount. + + + + The last name of the user who will be using the subaccount. + + + + Whether you will be able to log in to the subaccount through the Dev Portal at `app.bland.ai`. + + This enables you to set up credit card information, view and monitor usage, and further manage the subaccount as needed. + + The subaccount user will not be able to log into the Dev Portal unless you explicitly enable it by adding them as an Authorized User. + + + +### Response + + + Whether the subaccount creation succeeded. + + + + The unique identifier for the newly created subaccount. + + + + The API key that the new subaccount can use to authenticate requests. + + This is the only information that the subaccount user will need to start using the API. + + Do not use this as an identifier for the subaccount, since it can be rotated. + + + + +```json +{ + "status": "success", + "subaccount_id": "1234567890", + "subaccount_key": "sub-sk-1234567890" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/tools-tool-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/tools-tool-id.mdx new file mode 100644 index 00000000000..5b17ec37b9a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/tools-tool-id.mdx @@ -0,0 +1,353 @@ +--- +title: "Update Custom Tool" +api: "POST https://api.bland.ai/v1/tools/{tool_id}" +description: "Change your Custom Tool's parameters and characteristics." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The ID of the Custom Tool you want to update. + + +### Body + + + This is the name that the AI using the tool will see. + + Some other internal tools are named `Speak`, `Wait`, `Transfer` and `Finish` - Custom Tools cannot share these names. + + We've made a list of reserved words that can confuse the AI that cannot be included: + - `input` + - `speak` + - `transfer` + - `switch` + - `wait` + - `finish` + - `press` + - `button` + - `say` + - `pause` + - `record` + - `play` + - `dial` + - `hang` + + Choosing too similar of names to the default tools could cause the AI to select the wrong one, so decriptive two to three-word names are preferred. + + + + + This is the description that the AI using the tool will see. + + Describe the effect of what the tool does or any special instructions. + + For reference, here are the default tools' descriptions: + - `Speak`: Talk to the person on the other end of the line + - `Press Buttons`: Presses buttons on phone. Each character is a different button. + - `Wait`: Wait and go silent for an extended period of time (only use if absolutely necessary). + - `Finish`: Say a goodbye message and end the call once completed. + + + + + This is the text that the AI will say while it uses the tool. + + For example, if the tool is a "GenerateQuote" tool, the speech might be "Please wait while I get you your quote." + + Since tools can be verbally interrupted, shorter messages that tell the user what the tool/AI are doing are best. + + Special Note: You can have the AI dynamically generate speech by defining `input.speech` in the `input_schema`. + + + +```json +{ + "input_schema": { + "example": { + "speech": "Checking your account details right now John!", + "name": "John Doe", + "email": "johndoe@gmail.com" + }, + "type": "object", + "properties": { + "speech": { + "type": "string" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["speech", "name", "email"] + } +} +``` + + + + + + + This is the endpoint of the external API that the tool will call. + + It must begin with `https://` and be a valid URL. + + + + + This is the HTTP method that the tool will use to call the external API. + + Valid options are `GET` and `POST`. + + + + + `SUPPORTS PROMPT VARIABLES` + + These are the headers that the tool will send to the external API. + + The headers must be in JSON format. + + Since prompt variables are supported, you can use them in the headers to send dynamic information to the external API. + + + +```json +// At the top level of your send call request you can define variables that you can access later using the double curly braces/dot syntax. +{ + "request_data": { + "api_key": "sk-1234567890" + }, + "tools": [ + { + "headers": { + "Authorization": "Bearer {{api_key}}" + } + } + ] +} +``` + + + + + + + `SUPPORTS PROMPT VARIABLES` + + This is the body that the tool will send to the external API. + + The body must be in JSON format. + + This is the most common place to use Prompt Variables with AI input. + + Note: `GET` requests do not have a body. + + + +```json + // AI-generated input is created as the `input` Prompt Variable - and the structure is defined by the input schema. + // `input` will match the structure of `input_schema.example` if it is defined. + { + "input_schema": { + "example": { + "name": "John Doe", + "email": "johndoe@gmail.com" + } + } + "body": { + "name": "{{input.name}}", + "email": "{{input.email}}" + } + } +``` + + + + + + + `SUPPORTS PROMPT VARIABLES` + + Append query parameters to the URL. + + The query must be in JSON format. + + This is generally used with GET requests and built-in Prompt Variables like `"{{phone_number}}"` or `"{{call_id}}"`. + + + +```json +// appends ?pn={{phone_number}}&callId={{call_id}} to the URL +{ + "query": { + "pn": "{{phone_number}}", + "callId": "{{call_id}}" + } +} +``` + + + + + + + This is the schema that the AI input must match for the tool to be used. + + The schema must be in JSON format. + + The schema is used to validate the AI input before the tool is used. + + If the AI input does not match the schema, the tool will not be used and the AI will move on to the next tool. + + `input_schema.example` can be used to enhance the AI's understanding of the input structure and helps significantly with structured or nested data. + + Special Note: `input_schema` does not require strict JSON schema structure, and creativity is encouraged. + + [Look here for a general guide on JSON schema structures.](https://json-schema.org/learn/getting-started-step-by-step) + + Non-traditional JSON schema structures are supported as well, like these examples: + - "options": "monday, wednesday, friday" + - "date": "YYYY-MM-DD" + - "time": "HH:MM:SS (AM|PM)" + - "phone_number": "+1XXX-XXX-XXXX" + + Agent input can be nested, and the will be transformed into JSON even if it's initially a string. + + + +```json +{ + "input_schema": { + "example": { + "name": "John Doe", + "email": "johndoe@gmail.com" + }, + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["name", "email"] + }, + // both of these methods are identical, since {{input}} will be transformed into JSON + "body": "{{input}}", + "body": { + "name": "{{input.name}}", + "email": "{{input.email}}" + } +} +``` + + + + + + + Define how you would like to extract data from the response. + + By default, the entire response body is stored in the `{{data}}` Prompt Variable. + + The path to the data you want must be in JSON Path format. Generally this means using dot notation to traverse the JSON object and is only required if you need to use that information on other tools or the response is too large. + + Example: + +```json + // If the external API response is: + { + "available_times": [ + { + "time": "10:00 AM", + "date": "2022-01-01" + }, + { + "time": "11:00 AM", + "date": "2022-01-01" + } + ], + "store_hours": { + "open": "9:00 AM", + "close": "5:00 PM" + }, + "address_info": { + "street": "123 Main St", + "city": "Anytown", + "state": "CA", + "zip": "12345" + } + } + + // You can extract new Prompt Variables like this: + { + "response": { + "available_times": "$.available_times", + "store_hours": "$.store_hours", + "address_info": "$.address_info", + "zip_code": "$.address_info.zip" + } + } + + // And then it'll automatically replace them elsewhere (like in the `task`/`prompt`) + { + "task": "The store is open from {{store_hours.open}} to {{store_hours.close}}.", + "prompt": "The store is located at {{address_info.street}}, {{address_info.city}}, {{address_info.state}} {{zip_code}}." + } +``` + + + + + This is the maximum time in milliseconds that the tool will wait for a response from the external API. + + If the external API does not respond within this time, the tool will fail and the AI will move on to the next tool. + + The default timeout is 10 seconds (10000 milliseconds). + + To always wait for a response, set the timeout to an extremely high value like 99999999. + + + +### Response + + + Whether the tool creation succeeded. + + + + A tool id that you can use to reference the tool in the future. + + In a Send Call request, you could pass this tool id in instead of the full Custom Tool object like so: + + ```json + { + "tools": [ + "TL-1234567890" // tool_id (instead of the full Custom Tool object) + ] + } + ``` + + + + +```json +{ + "status": "success", + "tool_id": "TL-1234567890" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/tools.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/tools.mdx new file mode 100644 index 00000000000..1024f820c77 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/tools.mdx @@ -0,0 +1,347 @@ +--- +title: "Create a Custom Tool" +api: "POST https://api.bland.ai/v1/tools" +description: "Create a Custom Tool that can take AI input and call external APIs." +--- + +### Headers + + + Your API key for authentication. + + +### Body + + + This is the name that the AI using the tool will see. + + Some other internal tools are named `Speak`, `Wait`, `Transfer` and `Finish` - Custom Tools cannot share these names. + + We've made a list of reserved words that can confuse the AI that cannot be included: + - `input` + - `speak` + - `transfer` + - `switch` + - `wait` + - `finish` + - `press` + - `button` + - `say` + - `pause` + - `record` + - `play` + - `dial` + - `hang` + + Choosing too similar of names to the default tools could cause the AI to select the wrong one, so decriptive two to three-word names are preferred. + + + + + This is the description that the AI using the tool will see. + + Describe the effect of what the tool does or any special instructions. + + For reference, here are the default tools' descriptions: + - `Speak`: Talk to the person on the other end of the line + - `Press Buttons`: Presses buttons on phone. Each character is a different button. + - `Wait`: Wait and go silent for an extended period of time (only use if absolutely necessary). + - `Finish`: Say a goodbye message and end the call once completed. + + + + + This is the text that the AI will say while it uses the tool. + + For example, if the tool is a "GenerateQuote" tool, the speech might be "Please wait while I get you your quote." + + Since tools can be verbally interrupted, shorter messages that tell the user what the tool/AI are doing are best. + + Special Note: You can have the AI dynamically generate speech by defining `input.speech` in the `input_schema`. + + + +```json +{ + "input_schema": { + "example": { + "speech": "Checking your account details right now John!", + "name": "John Doe", + "email": "johndoe@gmail.com" + }, + "type": "object", + "properties": { + "speech": { + "type": "string" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["speech", "name", "email"] + } +} +``` + + + + + + + This is the endpoint of the external API that the tool will call. + + It must begin with `https://` and be a valid URL. + + + + + This is the HTTP method that the tool will use to call the external API. + + Valid options are `GET` and `POST`. + + + + + `SUPPORTS PROMPT VARIABLES` + + These are the headers that the tool will send to the external API. + + The headers must be in JSON format. + + Since prompt variables are supported, you can use them in the headers to send dynamic information to the external API. + + + +```json +// At the top level of your send call request you can define variables that you can access later using the double curly braces/dot syntax. +{ + "request_data": { + "api_key": "sk-1234567890" + }, + "tools": [ + { + "headers": { + "Authorization": "Bearer {{api_key}}" + } + } + ] +} +``` + + + + + + + `SUPPORTS PROMPT VARIABLES` + + This is the body that the tool will send to the external API. + + The body must be in JSON format. + + This is the most common place to use Prompt Variables with AI input. + + Note: `GET` requests do not have a body. + + + +```json + // AI-generated input is created as the `input` Prompt Variable - and the structure is defined by the input schema. + // `input` will match the structure of `input_schema.example` if it is defined. + { + "input_schema": { + "example": { + "name": "John Doe", + "email": "johndoe@gmail.com" + } + } + "body": { + "name": "{{input.name}}", + "email": "{{input.email}}" + } + } +``` + + + + + + + `SUPPORTS PROMPT VARIABLES` + + Append query parameters to the URL. + + The query must be in JSON format. + + This is generally used with GET requests and built-in Prompt Variables like `"{{phone_number}}"` or `"{{call_id}}"`. + + + +```json +// appends ?pn={{phone_number}}&callId={{call_id}} to the URL +{ + "query": { + "pn": "{{phone_number}}", + "callId": "{{call_id}}" + } +} +``` + + + + + + + This is the schema that the AI input must match for the tool to be used. + + The schema must be in JSON format. + + The schema is used to validate the AI input before the tool is used. + + If the AI input does not match the schema, the tool will not be used and the AI will move on to the next tool. + + `input_schema.example` can be used to enhance the AI's understanding of the input structure and helps significantly with structured or nested data. + + Special Note: `input_schema` does not require strict JSON schema structure, and creativity is encouraged. + + [Look here for a general guide on JSON schema structures.](https://json-schema.org/learn/getting-started-step-by-step) + + Non-traditional JSON schema structures are supported as well, like these examples: + - "options": "monday, wednesday, friday" + - "date": "YYYY-MM-DD" + - "time": "HH:MM:SS (AM|PM)" + - "phone_number": "+1XXX-XXX-XXXX" + + Agent input can be nested, and the will be transformed into JSON even if it's initially a string. + + + +```json +{ + "input_schema": { + "example": { + "name": "John Doe", + "email": "johndoe@gmail.com" + }, + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["name", "email"] + }, + // both of these methods are identical, since {{input}} will be transformed into JSON + "body": "{{input}}", + "body": { + "name": "{{input.name}}", + "email": "{{input.email}}" + } +} +``` + + + + + + + Define how you would like to extract data from the response. + + By default, the entire response body is stored in the `{{data}}` Prompt Variable. + + The path to the data you want must be in JSON Path format. Generally this means using dot notation to traverse the JSON object and is only required if you need to use that information on other tools or the response is too large. + + Example: + +```json + // If the external API response is: + { + "available_times": [ + { + "time": "10:00 AM", + "date": "2022-01-01" + }, + { + "time": "11:00 AM", + "date": "2022-01-01" + } + ], + "store_hours": { + "open": "9:00 AM", + "close": "5:00 PM" + }, + "address_info": { + "street": "123 Main St", + "city": "Anytown", + "state": "CA", + "zip": "12345" + } + } + + // You can extract new Prompt Variables like this: + { + "response": { + "available_times": "$.available_times", + "store_hours": "$.store_hours", + "address_info": "$.address_info", + "zip_code": "$.address_info.zip" + } + } + + // And then it'll automatically replace them elsewhere (like in the `task`/`prompt`) + { + "task": "The store is open from {{store_hours.open}} to {{store_hours.close}}.", + "prompt": "The store is located at {{address_info.street}}, {{address_info.city}}, {{address_info.state}} {{zip_code}}." + } +``` + + + + + This is the maximum time in milliseconds that the tool will wait for a response from the external API. + + If the external API does not respond within this time, the tool will fail and the AI will move on to the next tool. + + The default timeout is 10 seconds (10000 milliseconds). + + To always wait for a response, set the timeout to an extremely high value like 99999999. + + + +### Response + + + Whether the tool creation succeeded. + + + + A tool id that you can use to reference the tool in the future. + + In a Send Call request, you could pass this tool id in instead of the full Custom Tool object like so: + + ```json + { + "tools": [ + "TL-1234567890" // tool_id (instead of the full Custom Tool object) + ] + } + ``` + + + + +```json +{ + "status": "success", + "tool_id": "TL-1234567890" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/voices-id-sample.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/voices-id-sample.mdx new file mode 100644 index 00000000000..21083ce2959 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/api-v1/post/voices-id-sample.mdx @@ -0,0 +1,45 @@ +--- +title: "Generate Audio Sample" +api: "POST https://api.bland.ai/v1/voices/{id}/sample" +description: "Generate an audio sample for a voice." +--- + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The ID of the voice to generate the audio sample for, or it's name (like "maya"). + + +### Request Body + + + The text content to be spoken in the voice sample. + +Character limit: `200` characters. + + + + + Alternate `voice_settings` can be passed in to override the preset's default settings. + + + + The language of the text content. Default is `ENG`. + +Some other language codes: "ESP", "GER", "FRE" + + + +### Response + + + The generated audio file of the spoken text using the specified or overridden voice preset settings. + + +```json (Generated audio file) ``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/application-portal.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/application-portal.png new file mode 100644 index 00000000000..d575bc8b4cf Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/application-portal.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/developerportal.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/developerportal.png new file mode 100644 index 00000000000..c906cdfd631 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/developerportal.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/development.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/development.mdx new file mode 100644 index 00000000000..41052c0af15 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/development.mdx @@ -0,0 +1,92 @@ +--- +title: "Development" +description: "Learn how to preview changes locally" +--- + +**Prerequisite** You should have installed Node.js (version 18.10.0 or higher). + +Step 1. Install Mintlify on your OS: + + + +```bash npm +npm i -g mintlify +``` + +```bash yarn +yarn global add mintlify +``` + + + +Step 2. Go to the docs are located (where you can find `mint.json`) and run the following command: + +```bash +mintlify dev +``` + +The documentation website is now available at `http://localhost:3000`. + +### Custom Ports + +Mintlify uses port 3000 by default. You can use the `--port` flag to customize the port Mintlify runs on. For example, use this command to run in port 3333: + +```bash +mintlify dev --port 3333 +``` + +You will see an error like this if you try to run Mintlify in a port that's already taken: + +```md +Error: listen EADDRINUSE: address already in use :::3000 +``` + +## Mintlify Versions + +Each CLI is linked to a specific version of Mintlify. Please update the CLI if your local website looks different than production. + + + +```bash npm +npm i -g mintlify@latest +``` + +```bash yarn +yarn global upgrade mintlify +``` + + + +## Deployment + +Unlimited editors available under the [Startup Plan](https://mintlify.com/pricing) + +You should see the following if the deploy successfully went through: + + + + + +## Troubleshooting + +Here's how to solve some common problems when working with the CLI. + + + + Update to Node v18. Run `mintlify install` and try again. + + +Go to the `C:/Users/Username/.mintlify/` directory and remove the `mint` +folder. Then Open the Git Bash in this location and run `git clone +https://github.com/mintlify/mint.git`. + +Repeat step 3. + + + + Try navigating to the root of your device and delete the ~/.mintlify folder. + Then run `mintlify dev` again. + + + +Curious about what changed in a CLI version? [Check out the CLI changelog.](/changelog/command-line) diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/bring-your-own-twilio.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/bring-your-own-twilio.mdx new file mode 100644 index 00000000000..3c1fa6163d9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/bring-your-own-twilio.mdx @@ -0,0 +1,13 @@ +--- +title: Bring your own Twilio +--- + +Enterprise customers can create and connect their own Twilio account to Bland. + +Features include: + +1. Full ownership of telephony infrastructure +2. Ability to connect to existing telephony infrastructure +3. Closer control of spam risk and any discounted rates already included on the account + +Reach out for enterprise access, here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-llm.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-llm.mdx new file mode 100644 index 00000000000..198aa7847ae --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-llm.mdx @@ -0,0 +1,35 @@ +--- +title: Fine-tuning & Custom LLMs +--- + +## Summary + +Bland will fine-tune a custom model for your enterprise using transcripts from succesful prior calls. Then Bland will host that LLM and provided dedicated infrastrucure to enable phone conversations with sub-second latency. + +Bland will also enable you to connect to a custom LLM & will host that LLM to drive latency down further. + +Get in touch here. + +## Background on fine-tuning + +Traditionally, most AI phone agents use private models from companies like OpenAI and Anthropic. Those LLMs are large, and perform best at following instructions and delivering high quality outputs. The downside, however, is they are very slow. Additionally, because they're general models, their personality, tone, and overall capabilities are limited. + +Conversely, open source models generally perform worse at a broad range of tasks. However, by fine-tuning an open-source model with examples of a given task, you can significantly improve it's performance at that task, even surpassing the capabilties of top-of-the-line models like GPT-4. + +## How do I fine-tune with Bland? + +To inquire about fine-tuning connect with the Bland team here. + +During the initial conversation you will discuss: + +1. The format of data we'll require +2. How long fine-tuning will take (typically one week) +3. Pricing (typically under five figures) + +## How do I bring my own LLM? + +The Bland team will advise on connection method, requirements for the connection, etc. + +Typically takes under 24 hours to set up after kickoff. + +Inquire here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-tts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-tts.mdx new file mode 100644 index 00000000000..489ebcfd7ad --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-tts.mdx @@ -0,0 +1,9 @@ +--- +title: Custom languages & voices +--- + +Enterprise customers can bring their own TTS service or work with Bland's engineering team to configure higher quality voice clones. + +Enterprise customers can also request foreign languages like German, Spanish, Italian, and Portoguese, and Bland's engineering team will set up different transcription and TTS to accomodate the request. + +Reach out for enterprise access, here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-twilio.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-twilio.mdx new file mode 100644 index 00000000000..c6e3cbdc3e1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/custom-twilio.mdx @@ -0,0 +1,51 @@ +--- +title: "Custom Twilio Integration" +description: "Connect Bland to your own Twilio account" +--- + +## Overview + +Enterprise customers can connect their own Twilio account to Bland. Easily bring over your existing phone numbers, integrations, and more. + +Pre-requisites: + +- Your own Twilio account +- An [Enterprise plan](https://forms.default.com/361589) with Bland + +## Step 1: Creating an Encrypted Key with your Twilio Credentials + +1. Go to your [Twilio Console](https://www.twilio.com/console) and get your Account SID and Auth Token. +2. Create an `encrypted_key` by [sending an API request](/api-v1/post/accounts) to Bland. + +This is the only time that your `encrypted_key` will be returned to you. Make sure to store it securely, and new keys will need to be generated if lost. + +## Step 2: Using the Encrypted Key in Outbound Calls + +Include `encrypted_key` in the headers (in addition to the `Authorization` header) of your API requests, and we'll use that account's credentials to make the call. + +For example: + +```json +{ + "Authorization": "BLAND_API_KEY", + "encrypted_key": "YOUR_ENCRYPTED_KEY" +} +``` + +Note: + +- You can set your `from` number in the API request - this will need to be a number owned by that Twilio account (and not one purchased through Bland). +- By default, we'll send calls from a randomly selected number in the specified Twilio account if a `from` is not specified. + +## Step 3: Uploading Inbound numbers + +1. Go to your [Twilio Console](https://www.twilio.com/console) and get your Twilio phone number(s). +2. Upload your numbers [through the API](/api-v1/post/inbound-insert). + +We'll validate that these numbers are owned by that account and add them to your Bland account. + +## Step 4: Configuring Inbound Numbers/Webhooks + +Note: When updating inbound numbers, the headers need to include the `encrypted_key` in addition to the `Authorization` header. Doing so makes sure the updates are applied to the correct Twilio account. + +Once you update an inbound number through the [Dev Portal](https://app.bland.ai) or [API](/api-v1/post/inbound-number-update), that number will be automatically configured to run on Bland's infrastructure. No additional steps are required! diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/enterprise-rate-limits.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/enterprise-rate-limits.mdx new file mode 100644 index 00000000000..0cbd343e005 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/enterprise-rate-limits.mdx @@ -0,0 +1,9 @@ +--- +title: Enterprise rate limits +--- + +By default, Bland customers can dispatch 1000 calls/day before hitting rate limits. + +Enterprise customers start at 20,000 calls per hour, and 100,000 calls per day. + +To raise your rate limits or discuss limits larger than what's offered on enterprise, reach out here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/unlimited-support.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/unlimited-support.mdx new file mode 100644 index 00000000000..0b9cb05fae3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/enterprise-features/unlimited-support.mdx @@ -0,0 +1,13 @@ +--- +title: Unlimited support +--- + +Bland enterprise customers receive access to a shared slack channel with Bland's engineering team and founders. Enterprise customers also receive the founders phone numbers. Finally, for enterprise customers, the team strives to respond to every message in under five minutes, while maintaining a 24-hour SLA. + +Bland's engineers will guide on: + +1. Using features like `dynamic_data` and `custom_tools` +2. Best practices for prompting & configuring the phone agent +3. Making customizations (e.g. bring your own twilio, custom languages & voices, etc.) + +Reach out for enterprise access, here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/code.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/code.mdx new file mode 100644 index 00000000000..371116115fb --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/code.mdx @@ -0,0 +1,37 @@ +--- +title: "Code Blocks" +description: "Display inline code and code blocks" +icon: "code" +--- + +## Basic + +### Inline Code + +To denote a `word` or `phrase` as code, enclose it in backticks (`). + +``` +To denote a `word` or `phrase` as code, enclose it in backticks (`). +``` + +### Code Block + +Use [fenced code blocks](https://www.markdownguide.org/extended-syntax/#fenced-code-blocks) by enclosing code in three backticks and follow the leading ticks with the programming language of your snippet to get syntax highlighting. Optionally, you can also write the name of your code after the programming language. + +```java HelloWorld.java +class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} +``` + +````md +```java HelloWorld.java +class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} +``` +```` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/images.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/images.mdx new file mode 100644 index 00000000000..e52bf9771e9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/images.mdx @@ -0,0 +1,56 @@ +--- +title: "Images and Embeds" +description: "Add image, video, and other HTML elements" +icon: "image" +--- + + + +## Image + +### Using Markdown + +The [markdown syntax](https://www.markdownguide.org/basic-syntax/#images) lets you add images using the following code + +```md +![title](/path/image.jpg) +``` + +Note that the image file size must be less than 5MB. Otherwise, we recommend hosting on a service like [Cloudinary](https://cloudinary.com/) or [S3](https://aws.amazon.com/s3/). You can then use that URL and embed. + +### Using Embeds + +To get more customizability with images, you can also use [embeds](/writing-content/embed) to add images + +```html + +``` + +## Embeds and HTML elements + + + +
+ + + +Mintlify supports [HTML tags in Markdown](https://www.markdownguide.org/basic-syntax/#html). This is helpful if you prefer HTML tags to Markdown syntax, and lets you create documentation with infinite flexibility. + + + +### iFrames + +Loads another HTML page within the document. Most commonly used for embedding videos. + +```html + +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/markdown.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/markdown.mdx new file mode 100644 index 00000000000..01d5f8652f7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/markdown.mdx @@ -0,0 +1,88 @@ +--- +title: "Markdown Syntax" +description: "Text, title, and styling in standard markdown" +icon: "text-size" +--- + +## Titles + +Best used for section headers. + +```md +## Titles +``` + +### Subtitles + +Best use to subsection headers. + +```md +### Subtitles +``` + + + +Each **title** and **subtitle** creates an anchor and also shows up on the table of contents on the right. + + + +## Text Formatting + +We support most markdown formatting. Simply add `**`, `_`, or `~` around text to format it. + +| Style | How to write it | Result | +| ------------- | ----------------- | --------------- | +| Bold | `**bold**` | **bold** | +| Italic | `_italic_` | _italic_ | +| Strikethrough | `~strikethrough~` | ~strikethrough~ | + +You can combine these. For example, write `**_bold and italic_**` to get **_bold and italic_** text. + +You need to use HTML to write superscript and subscript text. That is, add `` or `` around your text. + +| Text Size | How to write it | Result | +| ----------- | ------------------------ | ---------------------- | +| Superscript | `superscript` | superscript | +| Subscript | `subscript` | subscript | + +## Linking to Pages + +You can add a link by wrapping text in `[]()`. You would write `[link to google](https://google.com)` to [link to google](https://google.com). + +Links to pages in your docs need to be root-relative. Basically, you should include the entire folder path. For example, `[link to text](/writing-content/text)` links to the page "Text" in our components section. + +Relative links like `[link to text](../text)` will open slower because we cannot optimize them as easily. + +## Blockquotes + +### Singleline + +To create a blockquote, add a `>` in front of a paragraph. + +> Dorothy followed her through many of the beautiful rooms in her castle. + +```md +> Dorothy followed her through many of the beautiful rooms in her castle. +``` + +### Multiline + +> Dorothy followed her through many of the beautiful rooms in her castle. +> +> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood. + +```md +> Dorothy followed her through many of the beautiful rooms in her castle. +> +> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood. +``` + +### LaTeX + +Mintlify supports [LaTeX](https://www.latex-project.org) through the Latex component. + +8 x (vk x H1 - H2) = (0,1) + +```md +8 x (vk x H1 - H2) = (0,1) +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/navigation.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/navigation.mdx new file mode 100644 index 00000000000..c08d564cf35 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/navigation.mdx @@ -0,0 +1,66 @@ +--- +title: "Navigation" +description: "The navigation field in mint.json defines the pages that go in the navigation menu" +icon: "map" +--- + +The navigation menu is the list of links on every website. + +You will likely update `mint.json` every time you add a new page. Pages do not show up automatically. + +## Navigation syntax + +Our navigation syntax is recursive which means you can make nested navigation groups. You don't need to include `.mdx` in page names. + + + +```json Regular Navigation +"navigation": [ + { + "group": "Getting Started", + "pages": ["quickstart"] + } +] +``` + +```json Nested Navigation +"navigation": [ + { + "group": "Getting Started", + "pages": [ + "quickstart", + { + "group": "Nested Reference Pages", + "pages": ["nested-reference-page"] + } + ] + } +] +``` + + + +## Folders + +Simply put your MDX files in folders and update the paths in `mint.json`. + +For example, to have a page at `https://yoursite.com/your-folder/your-page` you would make a folder called `your-folder` containing an MDX file called `your-page.mdx`. + + + +You cannot use `api` for the name of a folder unless you nest it inside another folder. Mintlify uses Next.js which reserves the top-level `api` folder for internal server calls. A folder name such as `api-reference` would be accepted. + + + +```json Navigation With Folder +"navigation": [ + { + "group": "Group Name", + "pages": ["your-folder/your-page"] + } +] +``` + +## Hidden Pages + +MDX files not included in `mint.json` will not show up in the sidebar but are accessible through the search bar and by linking directly to them. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/settings.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/settings.mdx new file mode 100644 index 00000000000..8525b5268eb --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/essentials/settings.mdx @@ -0,0 +1,317 @@ +--- +title: "Global Settings" +description: "Mintlify gives you complete control over the look and feel of your documentation using the mint.json file" +icon: "gear" +--- + +Every Mintlify site needs a `mint.json` file with the core configuration settings. Learn more about the [properties](#properties) below. + +## Properties + + +Name of your project. Used for the global title. + +Example: `mintlify` + + + + + An array of groups with all the pages within that group + + + The name of the group. + + Example: `Settings` + + + + The relative paths to the markdown files that will serve as pages. + + Example: `["customization", "page"]` + + + + + + + + Path to logo image or object with path to "light" and "dark" mode logo images + + + Path to the logo in light mode + + + Path to the logo in dark mode + + + Where clicking on the logo links you to + + + + + + Path to the favicon image + + + + Hex color codes for your global theme + + + The primary color. Used for most often for highlighted content, section headers, accents, in light mode + + + The primary color for dark mode. Used for most often for highlighted content, section headers, accents, in dark + mode + + + The primary color for important buttons + + + The color of the background in both light and dark mode + + + The hex color code of the background in light mode + + + The hex color code of the background in dark mode + + + + + + + + Array of `name`s and `url`s of links you want to include in the topbar + + + The name of the button. + + Example: `Contact us` + + + The url once you click on the button. Example: `https://mintlify.com/contact` + + + + + + + + + Link shows a button. GitHub shows the repo information at the url provided including the number of GitHub stars. + + + If `link`: What the button links to. + + If `github`: Link to the repository to load GitHub information from. + + + Text inside the button. Only required if `type` is a `link`. + + + + + + + Array of version names. Only use this if you want to show different versions of docs with a dropdown in the navigation + bar. + + + + An array of the anchors, includes the `icon`, `color`, and `url`. + + + The [Font Awesome](https://fontawesome.com/search?s=brands%2Cduotone) icon used to feature the anchor. + + Example: `comments` + + + The name of the anchor label. + + Example: `Community` + + + The start of the URL that marks what pages go in the anchor. Generally, this is the name of the folder you put your pages in. + + + The hex color of the anchor icon background. Can also be a gradient if you pass an object with the properties `from` and `to` that are each a hex color. + + + Used if you want to hide an anchor until the correct docs version is selected. + + + Pass `true` if you want to hide the anchor until you directly link someone to docs inside it. + + + One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin" + + + + + + + Override the default configurations for the top-most anchor. + + + The name of the top-most anchor + + + Font Awesome icon. + + + One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin" + + + + + + An array of navigational tabs. + + + The name of the tab label. + + + The start of the URL that marks what pages go in the tab. Generally, this is the name of the folder you put your + pages in. + + + + + + Configuration for API settings. Learn more about API pages at [API Components](/api-playground/demo). + + + The base url for all API endpoints. If `baseUrl` is an array, it will enable for multiple base url + options that the user can toggle. + + + + + + The authentication strategy used for all API endpoints. + + + The name of the authentication parameter used in the API playground. + + If method is `basic`, the format should be `[usernameName]:[passwordName]` + + + The default value that's designed to be a prefix for the authentication input field. + + E.g. If an `inputPrefix` of `AuthKey` would inherit the default input result of the authentication field as `AuthKey`. + + + + + + Configurations for the API playground + + + + Whether the playground is showing, hidden, or only displaying the endpoint with no added user interactivity `simple` + + Learn more at the [playground guides](/api-playground/demo) + + + + + + Enabling this flag ensures that key ordering in OpenAPI pages matches the key ordering defined in the OpenAPI file. + + This behavior will soon be enabled by default, at which point this field will be deprecated. + + + + + + + A string or an array of strings of URL(s) or relative path(s) pointing to your + OpenAPI file. + + Examples: + + ```json Absolute + "openapi": "https://example.com/openapi.json" + ``` + ```json Relative + "openapi": "/openapi.json" + ``` + ```json Multiple + "openapi": ["https://example.com/openapi1.json", "/openapi2.json", "/openapi3.json"] + ``` + + + + + + An object of social media accounts where the key:property pair represents the social media platform and the account url. + + Example: + ```json + { + "twitter": "https://twitter.com/mintlify", + "website": "https://mintlify.com" + } + ``` + + + One of the following values `website`, `facebook`, `twitter`, `discord`, `slack`, `github`, `linkedin`, `instagram`, `hacker-news` + + Example: `twitter` + + + The URL to the social platform. + + Example: `https://twitter.com/mintlify` + + + + + + Configurations to enable feedback buttons + + + + Enables a button to allow users to suggest edits via pull requests + + + Enables a button to allow users to raise an issue about the documentation + + + + + + Customize the dark mode toggle. + + + Set if you always want to show light or dark mode for new users. When not + set, we default to the same mode as the user's operating system. + + + Set to true to hide the dark/light mode toggle. You can combine `isHidden` with `default` to force your docs to only use light or dark mode. For example: + + + ```json Only Dark Mode + "modeToggle": { + "default": "dark", + "isHidden": true + } + ``` + + ```json Only Light Mode + "modeToggle": { + "default": "light", + "isHidden": true + } + ``` + + + + + + + + + A background image to be displayed behind every page. See example with [Infisical](https://infisical.com/docs) and + [FRPC](https://frpc.io). + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/examples/call.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/examples/call.mdx new file mode 100644 index 00000000000..039740c8f7b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/examples/call.mdx @@ -0,0 +1,144 @@ +--- +title: "Call an inbound lead" +description: "Learn to use the `/call` endpoint to contact an inbound lead to your website" +--- + + + +## Introduction + +Imagine you're selling a service, online. Your website contains a contact form where potential customers can request a demo. You know deals will close way more often if you call leads within 5 minutes of their form submission. Unfortunately, you don't have enough hands on deck to make those calls yourself, so you've decided to automate those calls with an AI voice agent. + +In this example, we'll show you how to call your inbound leads with a personalized prompt. Let's get started! + +## Prompt for the phone call + +Your phone call prompt should be written as a [template string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) (a string that you can pass variables to). That way, you can insert the data captured from the form into your prompt. + +Here's what your prompt should look like (before you insert the data from the form). + + +Your name is Matt, and you're an AI phone agent from Acme Inc. Whenever an inbound lead visits your website, you call them within five minutes. You're direct in your manner of speech. You never repeat yourself. You follow your script as tightly as possible to keep the call on track. + +Here's how the typical phone call progresses: + +1. You introduce yourself +2. You ask qualification customers based on the customer type (startup, small business, enterprise) +3. If the customer is qualified, you explain that you'll text them a scheduling link right after the call +4. You end the phone call + +**Example dialogue** + +You: Hello! + +Person: Hi, who is this? + +You: Hi, this is Matt from Acme Inc. We specialize in professional window washing services. Is this `${first_name}` I'm speaking with? + +Person: Hi, yes it is. Nice to meet you Matt. + +You: Nice to meet you too, `${first_name}`. I'm calling to learn a bit about your needs regarding window cleaning. You're seeking services for a `${business_type}`, is that right? + +Person: Yes, that's right + +You: Great, thanks for sharing that. `${business_type}` are a key focus for us. Can you tell me more about your premises? How many windows or floors are we talking about? + +Person: We have a two-story building with quite a few windows. + +You: Sounds like something we can definitely help with. How often do you currently have your windows cleaned? + +Person: We haven't had a regular service; it's been a bit sporadic. + +You: Understood. Regular cleaning can really enhance the appearance and light in your workspace. What's prompting you to consider a regular service now? + +Person: We want to maintain a more professional look consistently. + +You: That makes sense. Maintaining a professional appearance is important. Regarding timelines, when would you ideally like to start the service? + +Person: As soon as possible, really. + +You: Perfect, we can certainly work with that. Lastly, if we find a solution that fits your needs, are you the decision-maker regarding this service, or is there someone else we should also speak with? + +Person: I'm the one handling this, so it's my call. + +You: Great! I can send you a link to our scheduling system so you can choose a time that works best for you. We can then come over for an assessment and provide a detailed quote. Does that sound good? + +Person: Yes, that works for me. + +You: Fantastic, I'll text you the link right away. Thanks for your time, `${first_name}`. We look forward to potentially working with you. + +Person: Thank you, Matt. Looking forward to it. + + + +Did you notice how detailed the prompt is? It provides so much context, and explains what character the AI phone agent is playing, why it's playing it, how it should speak, how the call should progress, and even provides an example dialogue. + +Now, check out what the prompt looks like after we insert the form variables. + + +Your name is Matt, and you're an AI phone agent from Acme Inc. Whenever an inbound lead visits your website, you call them within five minutes. You're direct in your manner of speech. You never repeat yourself. You follow your script as tightly as possible to keep the call on track. + +Here's how the typical phone call progresses: + +1. You introduce yourself +2. You ask qualification customers based on the customer type (startup, small business, enterprise) +3. If the customer is qualified, you explain that you'll text them a scheduling link right after the call +4. You end the phone call + +**Example dialogue** + +You: Hello! + +Person: Hi, who is this? + +You: Hi, this is Matt from Acme Inc. We specialize in professional window washing services. Is this Jordan I'm speaking with? + +Person: Hi, yes it is. Nice to meet you Matt. + +You: Nice to meet you too, Jordan. I'm calling to learn a bit about your needs regarding window cleaning. You're seeking services for a small business, is that right? + +Person: Yes, that's right + +You: Great, thanks for sharing that. Small businesses are a key focus for us. Can you tell me more about your premises? How many windows or floors are we talking about? + +Person: We have a two-story building with quite a few windows. + +You: Sounds like something we can definitely help with. How often do you currently have your windows cleaned? + +Person: We haven't had a regular service; it's been a bit sporadic. + +You: Understood. Regular cleaning can really enhance the appearance and light in your workspace. What's prompting you to consider a regular service now? + +Person: We want to maintain a more professional look consistently. + +You: That makes sense. Maintaining a professional appearance is important. Regarding timelines, when would you ideally like to start the service? + +Person: As soon as possible, really. + +You: Perfect, we can certainly work with that. Lastly, if we find a solution that fits your needs, are you the decision-maker regarding this service, or is there someone else we should also speak with? + +Person: I'm the one handling this, so it's my call. + +You: Great! I can send you a link to our scheduling system so you can choose a time that works best for you. We can then come over for an assessment and provide a detailed quote. Does that sound good? + +Person: Yes, that works for me. + +You: Fantastic, I'll text you the link right away. Thanks for your time, Jordan. We look forward to potentially working with you. + +Person: Thank you, Matt. Looking forward to it. + + + +## Customizing the phone agent + +In this case, our phone agent's name is Matt. So we'll use a male voice. + +Note, by default, the voice latency parameter `reduce_latency` is set to `true`. And the default `voice_id` is `0` (a male voice). Therefore, we'll just use the default voice settings. + +For clarity's sake, we'll include those in our code example anyone. + +## Code example + +The code example is present on the right hand section of this page. You can copy it, either in Javascript or Python. + +Have additional questions? Want to see other use cases? Join our [discord community](https://discord.gg/QvxDz8zcKe)! diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/examples/inboundlead.jpg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/examples/inboundlead.jpg new file mode 100644 index 00000000000..b9407a1a297 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/examples/inboundlead.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/favicon.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/favicon.png new file mode 100644 index 00000000000..84c7348cbfb Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/favicon.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/featured-guides/bland-and-botpress.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/featured-guides/bland-and-botpress.mdx new file mode 100644 index 00000000000..e066964be12 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/featured-guides/bland-and-botpress.mdx @@ -0,0 +1,23 @@ +--- +title: Bland & Botpress +--- + +## Overview + +The integration process involves several key steps, starting with setting up your Botpress server and configuring your Bland AI account to ensure both platforms can communicate effectively. You'll learn how to authenticate your Botpress instance with Bland AI, send dynamic data (like phone numbers and intents) from chatbot conversations to Bland AI, and initiate voice calls based on user inputs or specific triggers within your chatbot workflows. **This guide is also a great way for no-coders to deploy a bland AI project easily, with little to no coding experience.** + +This guide will cover: + +1. Setting Up Botpress: Instructions on preparing your Botpress environment for integration, including installation and basic configuration. +2. Configuring Bland AI: How to set up your Bland AI account, obtain necessary API keys, and understand the API's capabilities related to voice interactions. +3. Integration Workflow: Step-by-step guidance on creating workflows in Botpress that interact with Bland AI's API, focusing on initiating voice calls to user-provided numbers. +4. Testing and Troubleshooting: Tips for testing your integrated solution to ensure it works as expected and troubleshooting common issues that may arise during the integration process. +5. Advanced Use Cases: Ideas and examples for leveraging this integration to create innovative chatbot applications that blend chat and voice interactions in unique and valuable ways. + +## Getting started + +[Read the entire guide](https://docs.google.com/document/d/1zn-89jYvpS238bQvp0XEfdV-s_txVlJIHrfIFhpNcJ4). + +## About Nort Labs + +Nort Labs is an agency that leverages AI technology to create bespoke marketing, design, and automation solutions for their customers. You can learn more about Nort's services by visitng their [website](https://nortlabs.com/). diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/images/checks-passed.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/images/checks-passed.png new file mode 100644 index 00000000000..3303c773646 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/images/checks-passed.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/images/hero-light.svg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/images/hero-light.svg new file mode 100644 index 00000000000..37b401ccac4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/images/hero-light.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo.png new file mode 100644 index 00000000000..6790583be04 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo/dark.svg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo/dark.svg new file mode 100644 index 00000000000..485b0131bcc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo/dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo/light.svg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo/light.svg new file mode 100644 index 00000000000..3334add5829 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/logo/light.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/mint.json b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/mint.json new file mode 100644 index 00000000000..2e4fc996204 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/mint.json @@ -0,0 +1,230 @@ +{ + "$schema": "https://mintlify.com/schema.json", + "name": "Bland AI", + "logo": { + "dark": "/logo/dark.svg", + "light": "/logo/light.svg" + }, + "favicon": "/logo.png", + "colors": { + "primary": "#6D64EF", + "light": "#847AF9", + "dark": "#0b0a18", + "background": { + "dark": "#0A141B" + }, + "anchors": { + "from": "#847AF9", + "to": "#847AF9" + } + }, + "topbarLinks": [ + { + "name": "Support", + "url": "mailto:hello@bland.ai" + } + ], + "topbarCtaButton": { + "name": "GET YOUR KEYS", + "url": "https://app.bland.ai" + }, + "tabs": [ + { + "name": "V1 API Reference", + "url": "api-v1" + } + ], + "anchors": [ + { + "name": "Community & Support", + "icon": "discord", + "url": "https://discord.gg/QvxDz8zcKe" + }, + { + "name": "Blog", + "icon": "newspaper", + "url": "https://www.bland.ai/blog" + }, + { + "name": "AMHLB Prompting Guide", + "icon": "file", + "url": "https://docs.google.com/document/d/19JVeaM3c9a0JzsjsnzaZ2BOoBve4IeWx-fARDAgaEm8/edit" + }, + { + "name": "Full Code Examples", + "icon": "github", + "url": "https://github.com/Bland-AI/Bland-AI-Cookbooks/tree/main" + }, + { + "name": "Zapier No-code integration", + "icon": "bolt", + "url": "https://zapier.com/apps/bland-ai/integrations" + }, + { + "name": "Enterprise inquiries", + "icon": "building", + "url": "https://forms.default.com/361589" + } + ], + "navigation": [ + { + "group": "Get Started", + "pages": ["welcome-to-bland", "starter-guide"] + }, + { + "group": "Bland Enterprise", + "pages": [ + "enterprise-features/custom-twilio", + "enterprise-features/custom-llm", + "enterprise-features/unlimited-support", + "enterprise-features/custom-tts", + "enterprise-features/bring-your-own-twilio", + "enterprise-features/enterprise-rate-limits" + ] + }, + { + "group": "Featured guides", + "pages": ["featured-guides/bland-and-botpress"] + }, + { + "group": "Basic Tutorials", + "pages": [ + "tutorials/pathways", + "tutorials/custom-tools", + "tutorials/send-first-call", + "tutorials/webhook-signing", + "tutorials/send-1000-calls-at-once", + "tutorials/dynamic-data", + "tutorials/max-duration", + "tutorials/live-transfer", + "tutorials/webhooks" + ] + }, + { + "group": "Basic", + "pages": ["api-v1/post/calls-simple", "api-v1/post/calls-simple-pathway"] + }, + { + "group": "Calls", + "pages": [ + "api-v1/post/calls", + "api-v1/post/calls-id-analyze", + "api-v1/post/calls-id-stop", + "api-v1/get/calls", + "api-v1/get/calls-id", + "api-v1/get/calls-id-recording", + "api-v1/get/calls-corrected-transcript" + ] + }, + { + "group": "Web Agents", + "pages": [ + "api-v1/post/agents", + "api-v1/post/agents-id", + "api-v1/post/agents-id-authorize", + "api-v1/post/agents-id-delete", + "api-v1/get/agents" + ] + }, + { + "group": "Inbound Numbers", + "pages": [ + "api-v1/post/inbound-purchase", + "api-v1/post/inbound-number-update", + "api-v1/get/inbound", + "api-v1/get/inbound-number" + ] + }, + { + "group": "Outbound Numbers", + "pages": ["api-v1/post/outbound-purchase", "api-v1/get/outbound"] + }, + { + "group": "Voices", + "pages": ["api-v1/get/voices", "api-v1/get/voices-id", "api-v1/post/voices-id-sample"] + }, + { + "group": "Custom Tools", + "pages": ["api-v1/post/tools", "api-v1/post/tools-tool-id", "api-v1/get/tools", "api-v1/get/tools-tool-id"] + }, + { + "group": "Custom Twilio Accounts", + "pages": [ + "api-v1/post/accounts", + "api-v1/post/accounts-delete", + "api-v1/post/inbound-insert", + "api-v1/post/inbound-number-delete" + ] + }, + { + "group": "Subaccounts", + "pages": [ + "api-v1/post/subaccounts", + "api-v1/post/subaccounts-id-transfer", + "api-v1/post/subaccounts-id-rotate", + "api-v1/post/subaccounts-id-disable", + "api-v1/get/subaccounts", + "api-v1/get/subaccounts-id" + ] + }, + { + "group": "Batches", + "pages": [ + "api-v1/post/batches", + "api-v1/post/batches-id-analyze", + "api-v1/post/batches-id-stop", + "api-v1/get/batches", + "api-v1/get/batches-id", + "api-v1/get/batches-id-analysis" + ] + }, + { + "group": "Primary Endpoints", + "pages": [ + "api-reference/endpoint/call", + "api-reference/endpoint/logs", + "api-reference/endpoint/hold", + "api-reference/endpoint/end", + "api-reference/endpoint/dynamic_validate", + "api-reference/endpoint/purchase", + "api-reference/endpoint/inbound_prompt", + "api-reference/endpoint/recording" + ] + }, + { + "group": "Batch Endpoints", + "pages": [ + "api-reference/batch-endpoint/batch", + "api-reference/batch-endpoint/batch_get", + "api-reference/batch-endpoint/batches_get", + "api-reference/batch-endpoint/batch_stop", + "api-reference/batch-endpoint/end_batches" + ] + }, + { + "group": "SMS", + "pages": [ + "api-v1/post/sms-submit-reg", + "api-v1/post/sms-check-registration", + "api-v1/post/sms-prompt-update", + "api-v1/post/sms-analyze", + "api-v1/post/sms-get-messages", + "api-v1/post/sms-toggle-human", + "api-v1/post/sms-webhook-update" + ] + }, + { + "group": "Account", + "pages": ["api-v1/get/me"] + }, + { + "group": "Hold Endpoint", + "pages": ["api-reference/hold-endpoint/hold"] + } + ], + "footerSocials": { + "twitter": "https://twitter.com/usebland", + "linkedin": "https://www.linkedin.com/company/bland-ai/", + "discord": "https://discord.gg/QvxDz8zcKe" + } +} diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/quickstart.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/quickstart.mdx new file mode 100644 index 00000000000..a3ece360a6f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/quickstart.mdx @@ -0,0 +1,67 @@ +--- +title: "Quickstart" +description: "Start building awesome documentation in under 5 minutes" +--- + +## Setup your development + +Learn how to update your docs locally and and deploy them to the public. + +### Edit and preview + + + + During the onboarding process, we created a repository on your Github with your docs content. You can find this + repository on our [dashboard](https://dashboard.mintlify.com). To clone the repository locally, follow these + [instructions](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) in + your terminal. + + + Previewing helps you make sure your changes look as intended. We built a command line interface to render these + changes locally. 1. Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) to preview the documentation + changes locally with this command: ``` npm i -g mintlify ``` 2. Run the following command at the root of your + documentation (where `mint.json` is): ``` mintlify dev ``` + + + +### Deploy your changes + + + + + Our Github app automatically deploys your changes to your docs site, so you don't need to manage deployments yourself. + You can find the link to install on your [dashboard](https://dashboard.mintlify.com). Once the bot has been + successfully installed, there should be a check mark next to the commit hash of the repo. + + + [Commit and push your changes to + Git](https://docs.github.com/en/get-started/using-git/pushing-commits-to-a-remote-repository#about-git-push) for your + changes to update in your docs site. If you push and don't see that the Github app successfully deployed your changes, + you can also manually update your docs through our [dashboard](https://dashboard.mintlify.com). + + + + +## Update your docs + +Add content directly in your files with MDX syntax and React components. You can use any of our components, or even build your own. + + + + + Add flair to your docs with personalized branding. + + + + Implement your OpenAPI spec and enable API user interaction. + + + + Draw insights from user interactions with your documentation. + + + + Keep your docs on your own website's subdomain. + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/send-phone-call.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/send-phone-call.png new file mode 100644 index 00000000000..f9d208f6f37 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/send-phone-call.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/starter-guide.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/starter-guide.mdx new file mode 100644 index 00000000000..c14830bbd72 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/starter-guide.mdx @@ -0,0 +1,74 @@ +--- +title: Starter guide +description: Learn how to use Bland's API in under five minutes +--- + +## Creating a developer account + + + +To get started, sign up on the developer portal. + +Enter your phone number and verification code. Finally, once your developer portal loads, go to the `Send phone call` page. + +## Sending your first phone call + + + +Although Bland is an API-first platform, the send phone call page provides a simple interface for quickly testing calls. On the left side you can adjust the call options and on the right hand side you can see how the code updates. + +Once you're satisfied with a call, copy the code on the right side (in Javascript, Python, or cURL) and add it to your application. + + + In the `Phone Number` field, enter your own phone number. + + For the task box either select one of the example prompts or write your own. For more instrucionts about prompting + your AI phone agent, read this blog post. + + + Scroll to the bottom of the page, and press the `Send call` button. Note, calls are charged at $0.12/minute, billed + to the exact second. + + + +To send a phone call programatically, read the API reference. + +## Testing your phone agent + + + +Once you've sent your first phone call, the next step is to test and improve the outputs from your phone agent. + +One way to test your agent is to send yourself test calls. A faster way, however, is to use the Bland AI testing suite. + + + + Select the model and language and insert your current prompt into the task box. + + + Start messaging your phone agent. Act like you're the person receiving the call, and purposefully ask edge-case + questions to throw the phone agent off. + + Based on the responses you receive, update the instructions in the prompt. + + +## Next steps + +You now know how to send and test phone calls, but you've only scratched the surface of Bland's capabilties. + +Areas for further exploration: + + + + Read the API reference. + + + Creating custom tools for interacting with external APIs, live, during phone calls. + + + Learn how to prompt the exact behavior you want from your phone agent. + + + See what people are building on Bland and get support from other users. + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/support.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/support.mdx new file mode 100644 index 00000000000..2736395980d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/support.mdx @@ -0,0 +1,7 @@ +--- +title: 24/7 Support +--- + +Receive direct support from the Bland AI engineering + founding team. All our enterprise customers have our founders' phone numbers, and receive rapid responses & help from engineering team members. + +For additional information, get in touch. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/testing.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/testing.png new file mode 100644 index 00000000000..600581d9c0d Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/testing.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/add_global_prompt.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/add_global_prompt.png new file mode 100644 index 00000000000..87a1b9d09d4 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/add_global_prompt.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/batchcalls.jpg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/batchcalls.jpg new file mode 100644 index 00000000000..bec46d668b1 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/batchcalls.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/condition_eg.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/condition_eg.png new file mode 100644 index 00000000000..db7e9f8302a Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/condition_eg.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/custom-tools.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/custom-tools.mdx new file mode 100644 index 00000000000..50745d241f2 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/custom-tools.mdx @@ -0,0 +1,315 @@ +--- +title: "Custom Tools" +description: "Interact with the real world mid-call using custom tools." +--- + +## Introduction + +Custom tools allow your agent to interact with any web API mid-call. Do things like: + + + + Dispatch SMS or emails using the person's contact info. + + + Set appointments using live calendar availability. + + + Generate support tickets in your issue tracker. + + + Update your CRM with relevant details during the call. + + + +## Background + +To understand how custom tools work, let's take a peek under the hood of the Bland AI phone agent. + +During the conversation, the phone agent is constantly listening to figure out when it's supposed to respond. When the phone agent realizes it's time to respond, it reviews the tools in its toolbox and picks between them. + +Those tools include a `speak`, `wait`, and `button press` tool. When you create a custom tool, you add it to the existing 'toolbox' for the phone agent to pick from. + +A few natural questions arise: + +1. How do I define my custom tool? +2. How do I make sure my tool gets picked at the right time? +3. How does information from the call get passed to my custom tool's API request? +4. How do I fill the silence (when my custom tool is running)? +5. How does the response from my custom tool get added to the call? + +Keep reading to find out. + +# Creating your custom tool + +## Defining the API request + +Imagine you're creating an AI phone agent to take restaurant orders. You want your phone agent to have the ability to place orders, by pinging your backend API. + +Here's what that request might look like: + +```json +{ + "method": "POST", + "url": "https://api.your-restaurant.com/orders", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer YOUR_API_KEY" + }, + "body": { + "items": [ + { + "name": "burger", + "patties": 2, + "quantity": 1 + }, + { + "name": "fry", + "size": "lg", + "quantity": 1 + } + ] + } +} +``` + +## From API request to custom tool + +The next step is to convert the API request into a custom tool. Custom tools have the following properties: + +- `name` - the agent will see this in the list of tools +- `description` - a short explanation of what the tool does +- `input_schema` - a JSON schema describing the input data +- `speech` (optional) - a string that will be spoken to the agent while your tool waits for a response +- `response_data` - An array of objects that describe how to extract data from the response. Within the response data, you can create variables that the phone agent can reference in its prompt. + +### Name & Description + +The agent will see the name in the list of tools. The name, plus the description, help the AI phone agent when it decides which tool to use. + +For this example we'll set the name to `PlaceOrder`, and the description to `Places the final order for the drive thru`. + +### Input Schema + +The input schema is critical. It defines the shape of the API request, the different inputs the request can take, and also includes an example (which helps our system when creating requests). + +Here's what the input schema could look like: + +```json +"input_schema": { + "example": { + "items": [ + { + "name": "burger", + "patties": 2, + "quantity": 3 + }, + { + "name": "fry", + "size": "lg", + "quantity": 2 + } + ] + }, + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "patties": { + "type": "integer" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ] + }, + "flavor": { + "type": "string", + "enum": [ + "chocolate", + "strawberry", + "vanilla" + ], + "default": "vanilla" + }, + "quantity": { + "type": "integer" + } + }, + "required": [ + "name", + "quantity" + ] + } + } + } +} +``` + +Two important notes about input schema: + +1. `input_schema` is converted into the variable `"{{input}}"` that you can use in the request body/query/headers +2. To access nested properties, use dot notation: `"{{input.property.subproperty}}"` + +Scroll down to see the full example. + +### Speech + +Because requesting external APIs might take a while, we enable you to define a `speech` property. The phone agent will say the `speech` while it makes the request. + +An example speech might look like: `Perfect, I'll schedule that right now, give me just a second.` + +For the restaurant ordering example, the speech could be `Thank you, placing that order now.` + +### Response data + +Once your API request comes back, you need to extract the response data, and then make the phone agent aware of the new information. + +The `data` field determines how you extract the data while the `name` field determines the variable name for reference in the prompt. + +Here's an example response data: + +```json +"response_data": [ + { + "name": "order_price", + "data": "$.price" + } +] +``` + +## Full example + +Below is the entire API request for sending a phone call using the outlined custom tool: + +```json +{ + "phone_number": "...", + // note that the returned value ({{order_price}}) in the task is populated only after the tool is run + "task": "You are taking a drive thru order from a customer. Find out everything that they want like a drive thru cashier. Continue until they say they're done. Repeat the full order back to them after that, and ask if that's correct. If they confirm that it's correct, then and only then will you place their order using the PlaceOrder tool. After you place it, tell them their order total and to pull forward. Their order price is {{order_price}}", + "first_sentence": "Hi, what can I get started for you today?", + "request_data": { + "menu": { + "burger": ["patties"], + "fry": ["size"], + "shake": ["flavor", "size"] + } + }, + "tools": [ + { + "name": "PlaceOrder", + "description": "Places the final order for the drive thru.", + "speech": "Thank you, placing that order now.", + "input_schema": { + "example": { + "items": [ + { + "name": "burger", + "patties": 2, + "quantity": 3 + }, + { + "name": "fry", + "size": "lg", + "quantity": 2 + } + ] + }, + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "patties": { + "type": "integer" + }, + "size": { + "type": "string", + "enum": ["sm", "md", "lg"] + }, + "flavor": { + "type": "string", + "enum": ["chocolate", "strawberry", "vanilla"], + "default": "vanilla" + }, + "quantity": { + "type": "integer" + } + }, + "required": ["name", "quantity"] + } + } + } + }, + "url": "https://api.your-restaurant.com/orders", + "method": "POST", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer ..." + }, + "body": { + "items": "{{input.items}}" + }, + "response_data": [ + { + "name": "order_price", + "data": "$.price" + } + ] + } + ] +} +``` + +# Frequently asked questions + + + + The phone agent refers to the input schema and the `example` within it. Both of those pieces of information provide context about what data to pass. + + Then the phone agent extracts the information from the transcript and passes it to the request body. + + You can improve the accuracy of the input data by creating a very clear `input_schema`. That includes providing a detailed `example` within. + + + +{" "} + + + The phone agent looks at the tool's name and description. Then it looks at the current context of the conversation to + decide whether using the tool makes sense. + + + + When the API response from the custom tool comes back, you can extract the API response and create variables. You can do this within the `response_data` property. + + Once you've given the variable a `name`, you can reference it in the prompt using double brackets (`{{}}`). + + Note, with the current setup, you might reference variables that have null values. Here's the restaurant example prompt: + + You are taking a drive thru order from a customer. Find out everything that they want like a drive thru cashier. Continue until they say they're done. Repeat the full order back to them after that, and ask if that's correct. If they confirm that it's correct, then and only then will you place their order using the PlaceOrder tool. After you place it, tell them their order total and to pull forward. Their order price is `{{order_price}}` + + In the above prompt, the `order_price` will be null until the data comes back. That's okay though. The prompt is structured to first take the order, then use the PlaceOrder tool, and then finally respond with the order price. + + By the time the phone agent is asked for the order price, it will have the information. + + Custom tools will continue getting more robust, to further prevent scenarios where variables without value from being referenced in prompts. + + + + +If you have any additional questions, reach out at hello@bland.ai and one of our engineers will help. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/default_node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/default_node.png new file mode 100644 index 00000000000..a40ca317443 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/default_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/dynamic-data.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/dynamic-data.mdx new file mode 100644 index 00000000000..6f07d980eca --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/dynamic-data.mdx @@ -0,0 +1,268 @@ +--- +title: "Dynamic Data" +description: "Interact with the real world by connecting your agent to external APIs." +--- + +## Introduction + +With Dynamic Data, you can make external API requests at the start and throughout your phone call. This allows you to load data from your database, or from any other API. You can then use that data in your AI responses, or to define circumstantial behavior for each call. + +Some examples of what Dynamic Data enables: + +- Maintain conversation history between calls +- Define behavior based on the user's location +- Handle real-time data like status updates or prices + + + +Here's how to make an [Inbound Agent](/api-v1/post/inbound-number-update) remember past conversations they've had with callers. There's a lot of other useful information we can integrate, like how long ago that call occurred and the call's duration. + +`Endpoint: POST https://api.bland.ai/v1/inbound/<>` + +```json +{ + "prompt": "You're a service agent working on behalf of Bland AI...", + "dynamic_data": [ + { + // First, retrieve the previous call's data + "url": "https://api.bland.ai/v1/calls", + "method": "GET", + "headers": { + "authorization": <> + }, + "query": { + // These parameters narrow down the search and will make the request faster + // as well as easier to understand during later analysis. + "from_number": "{{phone_number}}", + "from": 1, // Offset by 1 to exclude the current call + "limit": 1 + }, + "response_data": [ + { + // These are the variables you're defining + "name": "previous_call_id", + // And the path to the data you want to extract + "data": "$.calls[0].c_id" + }, + { + "name": "previous_call_time", + "data": "$.calls[0].created_at" + }, + { + "name": "previous_call_duration_minutes", + "data": "$.calls[0].call_length" + } + ] + }, + { + // Once we have the previous call's ID, we can retrieve the transcript + // Note the variable used in the URL + "url": "https://api.bland.ai/v1/calls/{{previous_call_id}}", + "method": "GET", + "headers": { + "authorization": <> + }, + "response_data": [ + { + "name": "previous_conversation", + "data": "$.concatenated_transcript", + // The context parameter elaborates on the variable's purpose and use + "context": "Your previous conversation with this person (if it exists): {{previous_conversation}}" + } + ] + }, + { + // Helpful tip for debugging: + // You can send the data to a webhook to see what it looks like + "url": "https://webhook.site/...", + "method": "POST", + // Setting cache to false means you'll be able to see the data change in real-time + "cache": false, + "body": { + // And here's the data we're sending + "call_id": "{{call_id}}", + "prev_call_id": "{{previous_call_id}}", + "now": "{{now}}", + "previous_call_time": "{{previous_call_time}}", + "previous_conversation": "{{previous_conversation}}" + } + // Additional note: + // Since no response_data is defined, latency isn't affected by this request + } + ] +} +``` + + + +We'll cover the following features in this section: + +- System variables +- External API requests +- Extracting data from responses +- Variables as parameters +- Chaining requests + +## System Variables + +Variables are defined with double curly braces, like `{{variable}}`. System variables are predefined variables that are available in every AI phone call. You can use them to access information about the current call, like the user's phone number or the current time. + +Note: Variables are NOT case sensitive, and outer spaces are trimmed automatically. + +Base variables: + +- `{{phone_number}}` - Always the other party's number +- `{{country}}` - The country code (ex. US) +- `{{state}}` - The state or province's abbreviation (ex. CA for California) +- `{{city}}` - The full city name, capitalized +- `{{zip}}` - The zip code +- `{{call_id}}` - The unique ID of the current call +- `{{now}}` +- `{{now_utc}}` +- `{{from}}` - The outbound number in E.164 format +- `{{to}}` - The inbound number in E.164 format +- `{{short_from}}` - outbound number with country code removed +- `{{short_to}}` - inbound number with country code removed + +Variables can be used mid-sentence, like this: + +```json +{ + "task": "... Today is {{now}} ..." +} +``` + +## External API Requests + +External API requests are defined in the `dynamic_data` parameter of your call request or inbound agent configuration. The `dynamic_data` parameter is an array of objects, where each object represents an API request. + +Here's a simple request that can be used to load public data about the current price of Bitcoin, then store it in a variable called `{{bitcoin_price}}`: + +```json +{ + "dynamic_data": [ + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json" + "response_data": [ + { + "name": "bitcoin_price", + "data": "$.bpi.USD.rate", + "context": "Current price of Bitcoin in USD is ${{bitcoin_price}}" + } + ] + } + ] +} +``` + + + - `timeout` - The maximum number of milliseconds to wait for a response. - Default: `2000` - `method` - The HTTP + method to use. - Defaults to `GET`, otherwise `POST` is allowed. - `headers` - An object of headers to send with the + request. - `body` - The body of the request. Only used if `method` is `POST`. - `response_data` - An array of objects + that define how to extract data from the response. - See the next section for more details. - `cache` - Whether to + store the response, or refresh that data before each AI response. - Defaults to `true`. + + +## Extracting Data from Responses + +Rather than using the full response, you can extract specific data from the response using the `data` parameter. The `data` parameter follows JSON structuring, using dot notation and array indices. For example, if the response is: + +```json +{ + "bpi": { + "USD": { + "code": "USD", + "rate": "9,000.00", + "description": "United States Dollar", + "rate_float": 9000 + }, + "GBP": { + "code": "GBP", + "rate": "6,000.00", + "description": "British Pound Sterling", + "rate_float": 6000 + } + } +} +``` + +Then `$.bpi.USD.rate` would return `9,000.00`. + +More complex filters can be used if they follow the [JSONPath format](https://docs.hevodata.com/sources/engg-analytics/streaming/rest-api/writing-jsonpath-expressions). + +## Variables as Parameters + +Once defined with `response_data`, variables can be used nearly anywhere. + +- In the `task` or `prompt` parameters +- In the `context` parameter of `response_data` +- In the `body`, `headers` and/or `query` parameter of each request + +Afterwards, in your webhooks and when retrieving call transcripts, the `variables` field will contain all variables that were defined during the call. + +By far, the easiest way to test out your `dynamic_data` configuration is via the [/dynamic_data/test](/api-reference/endpoint/dynamic_validate) endpoint. It returns the original configuration, every raw response, and the final variables after parsing is applied. + +## Chaining Requests + +Each request is executed in order, and variables defined in previous requests can be used in the next request. For example, if you want to retrieve information from your database or ours, then take additional actions with that data then you could do something like the following: + +For this example, imagine a delivery service that offers instant checkout for customers that have signed up to be a member. The first request retrieves their member_id from your database like so: + +```json +{ + "dynamic_data": [ + { + "url": "https://api.restaurant.com/customers", + "method": "GET", + "headers": { + "authorization": "ExtremelySecureCredentials" + }, + "query": { + "phone_number": "{{phone_number}}" + }, + "response_data": [ + { + "name": "member_id", + "data": "$.customer.member_id" + } + ] + } + //... + ] +} +``` + +We just created that `{{member_id}}` variable - now we can use it in the next request. + +This delivery service also can be called to check on an order status. + +Note a difference: The `cache` parameter is set to `false`, so if the order status changes during the call, the agent will immediately know about it and be able to inform the customer. + +```json +{ + "dynamic_data": [ + //... + { + "url": "https://api.restaurant.com/orders", + "method": "GET", + "cache": false, + "headers": { + "authorization": "ExtremelySecureCredentials" + }, + "query": { + "member_id": "{{member_id}}" + }, + "response_data": [ + { + "name": "order_id", + "data": "$.orders[0].id" + }, + { + "name": "order_status", + "data": "$.orders[0].status" + } + ] + } + ] +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/edges.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/edges.png new file mode 100644 index 00000000000..124e4291809 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/edges.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/end_node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/end_node.png new file mode 100644 index 00000000000..4a0c2d395d2 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/end_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/extract_variables.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/extract_variables.png new file mode 100644 index 00000000000..14f1530e29f Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/extract_variables.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/final_pathway.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/final_pathway.png new file mode 100644 index 00000000000..9f592f20658 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/final_pathway.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/finetune.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/finetune.png new file mode 100644 index 00000000000..fc5d7d3f425 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/finetune.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/finetuned.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/finetuned.png new file mode 100644 index 00000000000..de014643589 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/finetuned.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/global_eg.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/global_eg.png new file mode 100644 index 00000000000..31436694447 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/global_eg.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/global_node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/global_node.png new file mode 100644 index 00000000000..40eb7a91fa9 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/global_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/inbound-number-webhook.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/inbound-number-webhook.png new file mode 100644 index 00000000000..7b3b9e0d406 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/inbound-number-webhook.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/kb_node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/kb_node.png new file mode 100644 index 00000000000..d496d9cc009 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/kb_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/live-transfer.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/live-transfer.mdx new file mode 100644 index 00000000000..9b884b696a9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/live-transfer.mdx @@ -0,0 +1,83 @@ +--- +title: "Live transfer" +--- + + + +## Introduction + +Implementing live transfer in your AI-powered phone calls enhances flexibility and customer experience. This guide will explain how to set up a live transfer during a call using Bland AI. + +## Step 1: Understand Live Transfer + +Live transfer allows the AI agent to transfer the call to a human representative under certain conditions. This is crucial for scenarios where human intervention is preferred. + +## Step 2: Setup Your Authorization + +Before initiating a live transfer, ensure your API key is ready. Obtain your key from the [developer portal](https://app.bland.ai) if you haven't already. + +## Step 3: Configure the Transfer Settings + +Include the `transfer_phone_number` parameter in your call data. This is the number the AI will transfer to. Additionally, define the conditions for transfer in the task parameter. + +Example: + +```javascript +{ + "phone_number": "+11233456789", + "transfer_phone_number": "+19876543210", + "task": "If the caller requests to speak with a human, transfer the call to the representative." +} +``` + +## Step 4: Send the API Request + +Make the API request using the JavaScript or Python code snippet provided, ensuring the `transfer_phone_number` and conditions are included. + +## Step 5: Test and Monitor + +After setting up the live transfer, test the functionality to ensure it works as expected. Monitor the calls to adjust the transfer conditions as necessary. + +## Conclusion + +Setting up a live transfer offers a seamless experience for situations where AI needs to hand over to a human. You're now ready to integrate this feature into your AI-powered calls with Bland AI. + +Maintain a balance between automated and human interactions to optimize customer satisfaction. Happy calling! + + + +```javascript LiveTransfer.js +// Headers +const headers = { + authorization: "YOUR-API-KEY-HERE", +}; + +// Data +const data = { + phone_number: "+11233456789", + transfer_phone_number: "+19876543210", + task: "If the caller requests to speak with a human, transfer the call to the representative.", +}; + +// API request +await axios.post("https://api.bland.ai/v1/calls", data, { headers }); +``` + +```python LiveTransfer.py +# Headers +headers = { + 'authorization': 'YOUR-API-KEY-HERE' +} + +# Data +data = { + 'phone_number': '+11233456789', + 'transfer_phone_number': '+19876543210', + 'task': 'If the caller requests to speak with a human, transfer the call to the representative.' +} + +# API request +response = requests.post('https://api.bland.ai/v1/calls', json=data, headers=headers) +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/live_call_logs.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/live_call_logs.png new file mode 100644 index 00000000000..bfa24e22ab4 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/live_call_logs.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/livetransfer.jpg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/livetransfer.jpg new file mode 100644 index 00000000000..63b16780d19 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/livetransfer.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/max-duration.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/max-duration.mdx new file mode 100644 index 00000000000..99c4d435d1a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/max-duration.mdx @@ -0,0 +1,85 @@ +--- +title: "Setting max duration" +--- + + + +## Introduction + +Controlling the length of your AI-powered phone calls is crucial for efficiency and cost-effectiveness. In this guide, we'll walk you through how to set a `max_duration` for your calls using Bland AI. + +## Step 1: Understand max_duration + +The `max_duration` parameter allows you to specify the longest duration you want your call to last, measured in minutes. Once the set duration is reached, the call will automatically end. + +## Step 2: Setup Your Authorization + +Ensure you have your API key ready for authentication. If you haven't obtained one, sign up on the [developer portal](app.bland.ai). + +## Step 3: Define max_duration in Your Call Data + +When preparing your call data, include the `max_duration` parameter. You can set it as a float or a string. + +Example values: `"30", "5.5", 45, 2.8`. + +Here’s how you might include it in your request data: + +```javascript +{ + "phone_number": "+11233456789", + "task": "Inquire about the latest product updates.", + "max_duration": "10", // Call lasts for 10 minutes max +} +``` + +## Step 4: Send the API Request + +Use the provided JavaScript or Python code snippet to make the API request, ensuring you include the `max_duration` parameter. + +## Step 5: Test and Adjust + +After setting `max_duration`, test your calls to ensure they're ending at the desired time. Adjust as necessary based on your needs and feedback. + +## Conclusion + +Setting a `max_duration` for your calls ensures they are concise and to the point, saving time and resources. You're now ready to efficiently manage the length of your AI-powered calls with Bland AI. + +Remember, the key is to find the right balance between giving enough time for meaningful interaction and keeping the calls concise. Happy calling! + + + +```javascript SendCall.js +// Headers +const headers = { + authorization: "YOUR-API-KEY-HERE", +}; + +// Data +const data = { + phone_number: "+11233456789", + task: "Inquire about the latest product updates.", + max_duration: "10", // Call lasts for 10 minutes max +}; + +// API request +await axios.post("https://api.bland.ai/v1/calls", data, { headers }); +``` + +```python SendCall.py +# Headers +headers = { + 'authorization': 'YOUR-API-KEY-HERE' +} + +# Data +data = { + 'phone_number': '+11233456789', + 'task': 'Inquire about the latest product updates.', + 'max_duration': '10', # Call lasts for 10 minutes max +} + +# API request +response = requests.post('https://api.bland.ai/v1/calls', json=data, headers=headers) +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/maxduration.jpg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/maxduration.jpg new file mode 100644 index 00000000000..648a1b301bb Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/maxduration.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/new_pathway.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/new_pathway.png new file mode 100644 index 00000000000..e6255897d8d Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/new_pathway.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/node.png new file mode 100644 index 00000000000..6bc933e0020 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathway_chat.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathway_chat.png new file mode 100644 index 00000000000..9467b610533 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathway_chat.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathway_label.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathway_label.png new file mode 100644 index 00000000000..1d7654a379a Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathway_label.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathwaylog_finetune.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathwaylog_finetune.png new file mode 100644 index 00000000000..b6df97a0c05 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathwaylog_finetune.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathways.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathways.mdx new file mode 100644 index 00000000000..1079d979270 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/pathways.mdx @@ -0,0 +1,285 @@ +--- +title: "Conversational Pathways" +description: "Gain greater control over your AI agent and the conversational flow. [Create a Pathway Now!](https://app.bland.ai/dashboard?page=convo-pathways)" +--- + +[Template Pathway Video Tutorial](https://www.loom.com/share/be9d6f1072ae4267abc0717e36e66078?sid=82a7843f-9e7c-457a-8f7b-4d8ca026e1ff) + +## Introduction + +Conversational pathways are our new way of prompting Bland that has led to major breakthroughs in realism. + + + + Give your agent instructions on how it should respond at specific points of the conversation. Choose between + prompting or fixed sentences. + + + Execute webhooks at any point during the conversation, and send speech during/after the webhook. + + + Control when your agent ends the call + + + Connect your agent to a knowledge base, to answer any questions the user has. + + + +# Terminologies + +To understand how pathways work, let's first understand the terminologies. + +## Nodes + +These blocks you see here are called Nodes. + + + +## Pathways + +Each of these dotted lines is called a Pathway. Their start end end points are the `Purple Circles` on the top and bottom of the nodes. + +In order to create a pathway from a node, you would click on the purple circle at the bottom of the node and drag your mouse to connect to the top purple circle another node. + +Upon doing so, you will now have a new dotted line connecting the two nodes, with a 'New Pathway' button in the middle of the line. + + + +In order to instruct the agent when to take this pathway, you would click on the Edit icon on the 'New Pathway' button, and input the conditions for when the agent should take this pathway. In the above example, the agent would take this pathway if the user is not available to talk, so I labelled the pathway as 'not a good time to talk'. + + + +And there you have it! You have now created a pathway from one node to another, and instructed the agent when to take this pathway. You can connect as many nodes as you want in this manner, and create as many pathways as you want. In order to create a new Node, press the 'Add new Node' button at the top-left of the screen. + +# How the Pathways Agent Works + +The agent starts at the first node, and then moves to the next node based on the pathway that the agent decides to take. The agent will then execute the instructions in the node as dialogue, and then move on to the next node based on the pathway that the agent decides to take. This process will continue until the agent decides to end the call. + +The agent will make decisions based on the labels you put in the pathways, when connecting one node to another, and the dialogue generated will be based on the instructions you set in the nodes. + + + +For Example, + +In this example, at the node named 'Ask for reservation info', the node asks for the user's reservation information. Based on the user's response, the agent will then move on to the next node based on the labels you put in the pathways. For the current node, it will check if the user has provided reservation information where the number of guests is either less than 8 or more than 8. If the user has provided reservation information where the number of guests is less than 8, the agent will move on to the node named 'Reservation booking'. If the user has provided reservation information where the number of guests is more than 8, the agent will move on to the node named 'Transfer Call'. The agent will then execute the instructions in the node as dialogue, and then move on to the next node based on the pathway that the agent decides to take. And the process repeats! + +## Conditions + +Conditions are a way to provide the agent with a condition that must be met in order for the agent to move on to the next node. If the condition is not met, the agent will stay on the same node and ensure the condition is met until the condition is fulfilled. + +Using the same example above, I set the condition for the 'Ask for reservation info' node as follows - "You must get the date, time, and number of guests for this reservation". This means that the agent will stay on the 'Ask for reservation info' node until the user provides the date, time, and number of guests for the reservation. If the user says something else or deviates from the conversation, the agent will stay on the 'Ask for reservation info' node and prompt the user to provide the date, time, and number of guests for the reservation. + +This helps you to ensure that the user provides the necessary information before moving on to the next node, and helps you to control the flow of the conversation. + + + +## Global Nodes + + + +Global Nodes take precendence over the condition decisions made by the agent. You can treat a global node as a node, that every other node in the pathway has a pathway to, with the label as the 'Global Pathway Label'. + +Using the Reservation Booking Example, if the user were to ask a question like 'What are the opening hours of the restaurant' when the agent is at the 'Ask for reservation info' node, the condition decision would not be met as the user did not provide the date, time, and number of guests for the reservation. However, the pathway label would be 'user has a question about the restaurant's hours or location', which links to a Global Node. As Global Nodes take precedence over the condition decision, the agent would then move to the 'Global Node' named 'Restaurant Questions' and provide the user with the opening hours of the restaurant. After providing the user with the opening hours of the restaurant, the agent would then automatically return to the 'Ask for reservation info' node, and continue with the flow of the conversation. + +This helps you to handle edge cases where the user might ask a question that is not related to the current conversation, and allows you to provide the user with the information they need, before returning to the conversation. + +Tip: The variables `{{lastUserMessage}}` and `{{prevNodePrompt}}` can be used in the Global Node to provide the agent with context on what the user said, and steering the conversation back to its own original goal. + + +You are to answer any questions the user has, to the best of your knowledge. If you do not know the answer, simply say 'I don't have that information at that moment'. Do not make up any information/facts about the appointment. + +After you answer the question, you are to direct the conversation back to your initial goal, which was as follows: + +Previous Goal: +`{{prevNodePrompt}}` + +If you deem the goal as achieved, you can simply ask the user 'So, shall we proceed?' . If the goal has not been achieved, you are to steer the conversation back to achieve your goal. +If you deem the goal as achieved, simply confirm the result with the user. If the goal has not been achieved, you are to steer the conversation back to achieve your goal. + +``` +Examples +-- +assistant: what date works for you? +user: what day is it today? +assistant: The day today is April 15th. So, what date works for you? +-- + +-- +assistant: what date works for you? +user: probably tomorrow, what time does the clinic open? +assistant: The clinic opens at 9am. So, do you want to schedule for tomorrow? +-- +``` + + + + + +# Node Types + +There are currently 6 different types of Nodes + +- Default +- Webhook +- Knowledge Base +- End Call +- Transfer Call +- Wait for Response + +You can select the type of node you want by clicking on the dropdown of `Node Type`. + +## Base/Default Node (Important!) + +The default node provides the ability to generate a response to the user. This functionality is exposed to all other nodes as well. + +You can either: + +- Use the `Prompt` field to give instructions on what the agent should do at this point in the conversation. This is the recommended way to generate responses as it makes the conversation more human and natural. +- Enable the 'Static Text' toggle to provide a fixed response, and the agent will always say the same thing at this point in the conversation. + + + +### Optional Decision Guide + +The optional decision guide is only to be used if your phone agent currently is not going down the correct pathway. We do not expect this to happen often, but still want to provide you the tools to handle these issues if they arise. + +It is a way to provide the phone agent with example scenarios of what the user might say, and what pathway the agent should take in response. + +You would put in examples of what the user might say in the `User Input` field, and then in the `Pathway` field, you would provide the pathway the agent should take in response. + +### Condition + +The condition is a way to provide the agent with a condition that must be met in order for the agent to move on to the next node. If the condition is not met, the agent will stay on the same node and ensure the condition is met until the condition is fulfilled. + +### Global Nodes + +Each node can be configured to be a Global Node. Global Nodes are nodes that are accessible by every other node in the Conversational Pathway. This means that it has an implicit pathway to every other node, and the label would be the 'Global Label'. + +After entering a Global Node, the agent will execute the instructions inside the Global Node, and then automatically return to the node it was at before entering the Global Node, so it can continue with the flow of the conversation. + +In a Global Node, you can also forward the agent to another node, by toggling the 'Enable Forwarding', which will allow you to select the node you want to move the agent to. This is useful if you want to move the agent to a existing node for a certain scenario, which could happen at any point in the conversation. + + + +## Transfer Call Node + +The transfer call node is used to transfer the call to another number when the node is reached, and the dialogue at this node is complete. + +As such, you may have the agent say any final words before the call is transferred. + + + +## End Call Node + +The end call node will end the call when the node is reached, and the dialogue at this node is complete. + +As such, you may have the agent say any final words before the call is ended. + + + +## Knowledge Base Node + +The knowledge base node is used to connect your agent to a knowledge base, to answer any questions the user has. + +Paste in any text in the 'Knowledge Base' field, and the agent will search through the knowledge base to answer the user. + +Coming soon - PDF Upload/Vector Database Integrations... + + + +## Wait for Response Node + +The Wait for Response Node works the same way as the Default Node, except it is also equipped with the ability to wait if the user requires time to respond or needs to hold for a moment. + +## Webhook Node + +The webhook node is used to execute webhooks at any point during the conversation, and send speech during/after the webhook. + +`Webhook Information` is all you need in order to execute a webhook, and works the same way as Dynamic Data. Refer to the [Dynamic Data](https://docs.bland.ai/tutorials/dynamic-data) section for more information... + +Similar to how the dialogue is handled in all other nodes, you can control the dialogue sent before, and after the webhook is executed. + +Variables received from the webhook can be used in the dialogue as well, as shown in the example below. + + + +# Global Prompt for all Nodes + +The 'Add Global Prompt to All Nodes' feature is to assist in providing context/instructions to the agent for all nodes, without having to manually input the same prompt for each node. One Example of a Global Prompt could be to provide the agent with instructions on how to handle the call, the tone of voice to use, or answering any questions the user might have. + + + +# Live Call Logs + +The live call logs are a way to provide you with a live feed of the conversation between the agent and the user. This is useful for debugging purposes, and to see how the agent is responding to the user in real-time and the decisions the agent is making. On top of the transcript, we expose the updated node the agent took, the pathway that the agent took, as well as whether the condition was met or not. + + + +# Testing the Pathway Agent via Chat + +You can test the responses from your pathway agent by clicking on the 'Chat with Pathway' button at the top right of the screen. This will open a chat window where you can test the agent by sending messages to the agent, and see how the agent responds. The live call logs will also be displayed on the right side of the screen, so you can see the decisions the agent is making in real-time. + + + +# Variables Reference/ Extraction + +## Variables + +You can reference variables in pathways using double curly braces like `{{first_name}}`. You can pass variables into your call by passing in the key-value pairs in the `request_data` field when sending a call. + +Some examples of variables that you can access at each node: + +- `{{lastUserMessage}}` - The last response from the user +- `{{prevNodePrompt}}` - The prompt from the previous node +- `{{now_utc}}` - The current time in UTC +- `{{from}}` - The phone number the call is from +- `{{to}}` - The phone number the call is to +- `{{call_id}}` - The unique identifier for the call + +## Extracting Variables from Call/User Response + +At each node, you can also extract variables from the user's response using the `Extract Variables from Call Info` field. You would put in the name of the variable you want to extract, the type of the variable (integer, string, boolean), and the description for what information you want the variable to store. You can also provide specific formats you expect and examples in the description. The more descriptive it is, the better the agent will be at extracting the variable more accurately. + +Do note that when enable variable extraction, and wanting to reference the variable in the subsequent nodes, it would introduce slight latency as the agent would have to extract the variable from the user's response before generating the dialogue for the next node. + +Within the webhook node, the variable extraction happens before the webhook is executed, so that you can reference the variables extracted from the user's response in the webhook's request data. The variables extracted from the webhook in `response_data` can also be referenced in the dialogue generated after the webhook is executed, in the same manner. + +For Example, the image below shows how the Webhook Node is set up to extract the date, time, and number of guests for the reservation, and how the extracted variables are referenced in the webhook's request data. + + + +# Fine Tuning the Pathway agent + +While Conversational Pathways gives you a lot greater control compared to the regular call agent, hallucinations can still occur, or the agent might make wrong decisions in the pathway. However, we have provided you with the tools to handle these issues if they arise. Each node can be fine-tuned on the decision it makes, as well as the expected dialogue it generates, allowing you to handle even the most extreme edge cases that might arise easily. + +### Steps to Fine-Tuning the agent + +Upon triggering and testing a call with your pathway using the 'Chat with Pathway' button, or sending a call, you will be able to see the live call logs. + +If you see a decision that the agent made that you do not agree with, or if the agent is hallucinating, you can fine-tune the decision the agent makes by clicking on the `Edit` button on the `PATHWAY DECISION INFO` block. + + + +Upon doing so, you will be able to see the decisions the agent made for the condition, the pathway it took, and the dialogue it generated. You can then fine-tune the agent by changing the dialogue, the condition, or the pathway the agent took. + +Upon saving your changes, you will be able to see the fine-tuning data in the node where the decision was made. This training data is used to train the agent to make better decisions in the future, and to ensure that the agent does not make the same mistake again. + + + +Do note that the decisions for the condition and pathway chosen is made by the node the agent is currently at, and the dialogue is generated by the node which the agent decides to take the pathway to. + +For Example, +If the agent is at Node 1, and the agent decides that the condition is achieved, and decides to take the pathway to Node 2, the dialogue generated will be from Node 2, and the decisions for the condition and pathway chosen will be from Node 1. As such, the training data for the condition and pathway chosen will be stored in Node 1, and amendments to the dialogue generated will be stored in Node 2. + + + +# Quick Start with Tutorial / Templates + +To immediately start playing around with Conversational Pathways, you can use one of our templates. Visit the Conversational Pathways page on our Dev Portal, and duplicate the 'Restaurant Reservation' Template, and run through the agent to see how it works! +We have also created a video walkthrough of that template to help you get started! + +[Video Walkthrough/Tutorial](https://www.loom.com/share/be9d6f1072ae4267abc0717e36e66078?sid=82a7843f-9e7c-457a-8f7b-4d8ca026e1ff) + +Create a pathway now! Click [here](https://app.bland.ai/dashboard?page=convo-pathways) to get started! + +If you have any additional questions, reach out on our [Discord](https://discord.gg/QvxDz8zcKe) and one of our engineers will help. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/send-1000-calls-at-once.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/send-1000-calls-at-once.mdx new file mode 100644 index 00000000000..adf2d3f2c80 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/send-1000-calls-at-once.mdx @@ -0,0 +1,114 @@ +--- +title: "Send 1000 phone calls" +--- + + + +## Step 1: Setup Your Authorization + +To initiate a batch call, you must first authenticate your request. Ensure you have your API key from signing up on the [developer portal](https://app.bland.ai). + +## Step 2: Create the Base Prompt + +Craft a base prompt that will be common across all calls in the batch. Use placeholders `{{curly braces}}` for dynamic content. + +Example: + +```javascript +"You are calling {{business}} to renew their subscription to {{service}} before it expires on {{date}}."; +``` + +## Step 3: Define the Call Data + +Specify the list of calls in the `call_data` array. Each call must have a `phone_number` and can include other properties corresponding to placeholders in your base prompt. + +Example: + +```javascript +[ + { + phone_number: "1234567890", + business: "ABC co.", + service: "Netflix", + date: "September 4th", + }, + { + phone_number: "32176540987", + business: "XYZ inc.", + service: "Window Cleaning", + date: "December 20th", + }, +]; +``` + +## Step 4: Additional Configuration + +- `label`: Assign a label to your batch for easy tracking. +- `campaign_id`: Organize related batches under a campaign. +- `test_mode`: Set to true for testing with the first call only. +- `batch_id`: Manually set or auto-generated for tracking. +- Voice and Language Settings: Select `voice_id`, `reduce_latency`, and `language`. +- `request_data`: Include specific facts for the AI to know during the call. +- `webook`: For real-time notifications and transcripts post-call. +- `max_duration`: Define the maximum length of each call. +- `amd`: Enable for navigating phone trees or leaving voicemails. +- `wait_for_greeting`: Control if the AI speaks immediately or waits. + +## Step 5: Send the API Request + +Use the provided JavaScript or Python code snippet to make the API request. + +## Step 6: Handle the Response + +After sending the batch request, you'll receive a response with a `message` and the `batch_id`. Monitor the progress of your calls and any responses via your specified webhook. + +Here's what an example response might look like: + +```javascript +{ + "message": "success", + "batch_id": "3p$7rQ3p9sT5bzmF-gen-batch" +} +``` + +## Step 7: Monitoring and Analytics + +Track the performance and outcomes of your batch calls through the provided `batch_id` and campaign analytics. Adjust future batches based on the insights gained. + + + +```javascript SendCalls.js +// Headers +const headers = { + authorization: "YOUR-API-KEY-HERE", +}; + +// Data +const data = { + base_prompt: "You are calling {{business}} to renew their subscription to {{service}} before it expires on {{date}}.", + call_data: [ + { + phone_number: "1234567890", + business: "ABC co.", + service: "Netflix", + date: "September 4th", + }, + { + phone_number: "32176540987", + business: "XYZ inc.", + service: "Window Cleaning", + date: "December 20th", + }, + ], + label: "Renewal Reminder - Wednesday Afternoon with female voice", + voice_id: 0, + max_duration: 10, + reduce_latency: true, + wait_for_greeting: true, +}; + +// API request +await axios.post("https://api.bland.ai/v1/batches", data, { headers }); +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/send-first-call.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/send-first-call.mdx new file mode 100644 index 00000000000..1295aa8b13d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/send-first-call.mdx @@ -0,0 +1,108 @@ +--- +title: "Send your first phone call" +--- + + + +## Step 1: Setup Your Authorization + +Before making a call, you need to authenticate your request. Make sure you have your API key ready. + +Sign up on the [developer portal](https://app.bland.ai) to get yours. + +## Step 2: Prepare the Call Data + +You will need to provide specific details for the call. These include: + +- `phone_number`: The number you want to call. Remember to include the country code. +- `task`: Describe the purpose of the call and how the AI should handle the conversation. +- `voice_id`: Choose the voice persona (American male, Australian female, etc.) based on your preference. +- Set parameters like `reduce_latency`, `record`, `amd` (for navigating phone trees), and `wait_for_greeting` according to your call's requirements. + +## Step 3: Customize the Call + +You can further personalize the call by: + +1. Setting a `first_sentence` for the call. +2. Specifying `dynamic_data` to incorporate external API data. +3. Adjusting `voice_settings` for stability, similarity, and speed. +4. Choosing the language with the `language` parameter. +5. Setting a `max_duration` for the call. + +## Step 4: Send the API Request + +Use the provided JavaScript or Python code snippet to make the API request. + +## Step 5: Handle the Response + +After the call, you will receive a response with the `status` and `call_id`. If you set `record` to true, you can retrieve the recording using the `/call/recording` endpoint. + +Here's what an example response might look like: + +```javascript +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + +## Step 6: Monitor the Call + +If you have set up a webhook, you will receive real-time notifications and transcripts once the call completes. + +And that's it! You're now ready to make your first AI-powered phone call with Bland AI. Happy calling! + + + +```javascript SendCall.js +// Headers +const headers = { + authorization: "YOUR-API-KEY-HERE", +}; + +// Data +const data = { + phone_number: "+11233456789", + task: "You are calling Fantastic Airlines on behalf of John Doe. Find out where John's Bags are located.", + voice_id: 0, + language: "eng", + request_data: { + calling: "Fantastic Airlines", + bag_claim: "69683", + airline_code: "UA123", + }, + record: true, + reduce_latency: true, + amd: true, +}; + +// API request +await axios.post("https://api.bland.ai/v1/calls", data, { headers }); +``` + +```python SendCall.py +# Headers +headers = { + 'authorization': 'YOUR-API-KEY-HERE' +} + +# Data +data = { + 'phone_number': '+11233456789', + 'task': "You are calling Fantastic Airlines on behalf of John Doe. Find the location of out where John's Bags are located.", + 'voice_id': 0, + 'request_data': { + 'calling': 'Fantastic Airlines', + 'bag_claim': '69683', + 'airline_code': 'UA123' + }, + 'record': True, + 'reduce_latency': True, + 'amd': True +} + +# API request +response = requests.post('https://api.bland.ai/v1/calls', json=data, headers=headers) +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/sendfirstcall.jpg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/sendfirstcall.jpg new file mode 100644 index 00000000000..a509bcd6f04 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/sendfirstcall.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/transfer_node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/transfer_node.png new file mode 100644 index 00000000000..dc3cf41c12d Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/transfer_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook-fires-successfully.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook-fires-successfully.png new file mode 100644 index 00000000000..97ada0559b0 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook-fires-successfully.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook-signing.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook-signing.mdx new file mode 100644 index 00000000000..a0e589e4c96 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook-signing.mdx @@ -0,0 +1,43 @@ +--- +title: Webhook Signing +--- + +Bland webhooks are signed with a secret key to ensure that they are not tampered with in transit and to confirm that they were sent by Bland. + +## Signing Webhooks + +When Bland sends a webhook, it calculates a signature using the HMAC algorithm with the SHA-256 hash function. The signature is then included in the `X-Webhook-Signature` header of the request. + +To create a webhook signing secret, first go to the [Account Settings in the Dev Portal](https://app.bland.ai/dashboard?page=settings) and click on the "Keys" tab. + +Here you can create a new secret by clicking "Replace Secret". It will only be shown once, so save it securely. + +## Verifying Webhooks + +To verify a webhook, you need to calculate the HMAC signature of the request body using the secret key and compare it to the signature in the `X-Webhook-Signature` header. + +Note that you must first create a webhook signing secret in the [Account Settings in the Dev Portal](https://app.bland.ai/dashboard?page=settings). + +Here is an example of how to verify a webhook in Node.js: + +```javascript +const crypto = require("node:crypto"); + +function verifyWebhookSignature(key, data, signature) { + const expectedSignature = crypto.createHmac("sha256", key).update(data).digest("hex"); + + return expectedSignature === signature; +} + +//... + +app.post("/webhook", (req, res) => { + const isValid = verifyWebhookSignature( + process.env.WEBHOOK_SECRET, + JSON.stringify(req.body), + req.headers["x-webhook-signature"], + ); + + //... +}); +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook.png new file mode 100644 index 00000000000..3791fac78a6 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook_node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook_node.png new file mode 100644 index 00000000000..dfeaa4cea5c Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhook_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhooks.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhooks.mdx new file mode 100644 index 00000000000..e6983442732 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/tutorials/webhooks.mdx @@ -0,0 +1,49 @@ +--- +title: "Webhooks" +--- + + + +## Introduction + +This is a quick tutorial to help you set up and test your Bland AI webhook. We'll include instructions both for inbound and outbound phone calls. + +We'll start with inbound because it's more popular. + +## Step 1: Create your webhook + +To create a test webhook visit [Webhook.site](https://webhook.site/) + +The website will automatically provide you a unique webhook URL. + +## Step 2: Connect to your inbound phone number + +Open your [developer portal](https://app.bland.ai) and visit the [inbound phone numbers](https://app.bland.ai/home?page=inbound-number) page. + + + +Paste your webhook into the `webhook` field. Make sure to remove the initial `https://` when you insert the URL. Then click `test webhook`. + +## Step 3: Verifying your outputs + +Navigate to webhook.site page, and check if the test webhook fired correctly. You'll know it worked because a new record will populate. + + + +At this point, if your record fails to populate, double check that you provided the correct URL - and that you REMOVED the initial `https://` from it. + +Otherwise, if issues persist, jump into the [discord](https://discord.gg/QvxDz8zcKe) - one of our teammates will help you asap. + +## Step 4: test a live phone call + +Call your inbound phone number. Once it ends, visit the Webhook site and confirm once again that a new record populated. + +If that's working, then you're set! + +## Step 5: Testing for outbound calls + +To test for outbound calls, once again create your webhook by referring back to step 1. + +Then, follow the [send phone call docs](https://docs.bland.ai/api-v1/post/calls) to create and send a phone call. Make sure you include the `webhook` as a parameter in your request. After, confirm that the webhook data populated on your webhook site page. + +And again, if you encounter issues, jump into [discord](https://discord.gg/QvxDz8zcKe) and message us - we will help asap. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/welcome-to-bland.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/welcome-to-bland.mdx new file mode 100644 index 00000000000..ed065b906ba --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/bland/welcome-to-bland.mdx @@ -0,0 +1,51 @@ +--- +title: Welcome to Bland AI +--- + +Bland is a platform for AI phone calling. Using our API, you can easily send or receive phone calls with a programmable voice agent. + +We really care about making our phone calls... + +1. **Fast**: sub-second latency from person speaking to AI responding. +2. **Reliable**: it's our responsibility, day in and day out, to make sure your phone calls work. No exceptions. +3. **Ultra flexible**: configure all aspects of your agent's behavior, by settings it's voice, creating transfer scenarios, configuring the initial greeting, etc. + +# What you can do with Bland + + + + Dispatch AI phone calls to call customers, leads, and to streamline operations. + + + Create inbound phone numbers for customer support, etc. + + + Connect external APIs and take live actions during phone calls. + + + Extract JSON data to answer questions about your calls. + + + Simultaneously send thousands of calls at once. + + + Fine-tune a custom LLM using your enterprise' call recordings and transcripts. + + + +# Getting started + + + + Read the API reference. + + + Learn to send your first phone call and test your agents. + + + Sign up on the developer portal. + + + Learn about enterprise features & meet with a member of the Bland AI team. + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/OpenApiSpec.yaml b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/OpenApiSpec.yaml new file mode 100644 index 00000000000..07cc51c6671 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/OpenApiSpec.yaml @@ -0,0 +1,4859 @@ +openapi: 3.0.0 +info: + title: Layer API + version: 1.0.0 + +paths: + /whoami: + get: + summary: 'Get client info' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RootInfoResponse' + /v1/activity: + get: + summary: 'Summary of activities for all businesses' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ClientActivitySummary' + /v1/activity/businesses/{businessId}: + get: + summary: 'Summary of business activities' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/BusinessActivitySummary' + + /v1/activity/businesses/{businessId}/activities: + get: + summary: 'List business activities' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTypedActivity' + + /v1/unit-transactions: + post: + summary: 'Import transactions' + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/FlatUnitTransactionInput' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/FlatUnitTransactionResult' + + /v1/businesses/{id}/auth-token: + get: + summary: 'Get business auth token' + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AuthToken' + + /v1/configure/plaid: + get: + summary: 'Get plaid client id and secret' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidConfigurationResponse' + put: + summary: 'Configure plaid client id and secret' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidConfigurationInput' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidConfigurationResponse' + + /v1/configure/stripe: + get: + summary: 'Get stripe secret' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/StripeConfigurationResponse' + put: + summary: 'Configure stripe secret' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/StripeConfigurationInput' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/StripeConfigurationResponse' + + /v1/businesses: + post: + summary: 'Create new business' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewBusinessParams' + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: '#/components/schemas/ApiBusiness' + get: + summary: 'List businesses' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiBusiness' + + /v1/businesses/{businessId}: + get: + summary: 'Get business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiBusiness' + put: + summary: 'Update business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BusinessUpdatePayload' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiBusiness' + + /v1/businesses/{businessId}/archive: + put: + summary: 'Archive business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiBusiness' + + /v1/businesses/{businessId}/categories: + get: + summary: 'List chart of accounts categories for business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CategoryList' + + /v1/businesses/{businessId}/balances: + get: + summary: 'Get chart of accounts for business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiChartOfAccountsOld' + + /v1/businesses/{businessId}/plaid/items: + get: + summary: 'List plaid items for business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiPlaidItem' + + /v1/businesses/{businessId}/plaid/items/{plaidItemPlaidId}: + delete: + summary: 'Delete plaid item from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: plaidItemPlaidId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/plaid/items/{plaidItemPlaidId}/configuration: + get: + summary: 'Get plaid item configuration' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: plaidItemPlaidId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidItemConfiguration' + put: + summary: 'Set plaid item configuration' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: plaidItemPlaidId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidItemConfiguration' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidItemConfiguration' + + /v1/businesses/{businessId}/plaid/items/{plaidItemPlaidId}/unlink: + post: + summary: 'Unlink and archive plaid item and child plaid accounts' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: plaidItemPlaidId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/plaid/link: + post: + summary: 'Create plaid item link token' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiLinkToken' + + /v1/businesses/{businessId}/plaid/link/exchange: + post: + summary: 'Exchange plaid token and add item to business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidLinkPublicToken' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/plaid/update-mode-link: + post: + summary: 'Update plaid link link' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateModeLinkCreationPayload' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiLinkToken' + + /v1/businesses/{businessId}/plaid/accounts: + get: + summary: 'List plaid accounts for business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiPlaidAccount' + + /v1/businesses/{businessId}/plaid/accounts/{plaidAccountId}/archive: + post: + summary: 'Archive plaid account' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: plaidAccountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiPlaidAccount' + + /v1/businesses/{businessId}/plaid/accounts/{plaidAccountId}/reactivate: + post: + summary: 'Reactivate archived plaid account' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: plaidAccountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiPlaidAccount' + + /v1/businesses/{businessId}/plaid/accounts/{plaidAccountId}/configuration: + get: + summary: 'Get plaid account configuration' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: plaidAccountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidAccountConfiguration' + put: + summary: 'Set plaid account configuration' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: plaidAccountId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidAccountConfiguration' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PlaidAccountConfiguration' + + /v1/businesses/{businessId}/bank-transactions: + get: + summary: 'List transactions from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiBankTransaction' + + /v1/businesses/{businessId}/bank-transactions/{transactionId}: + get: + summary: 'Get transaction from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: transactionId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiBankTransaction' + + /v1/businesses/{businessId}/bank-transactions/{transactionId}/categorize: + put: + summary: 'Categorize transaction' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: transactionId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BankTransactionCategorization' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiBankTransaction' + + /v1/businesses/{businessId}/bank-transactions/{transactionId}/uncategorize: + put: + summary: 'Uncategorize transaction' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: transactionId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiBankTransaction' + + /v1/businesses/{businessId}/bank-transactions/{transactionId}/match: + put: + summary: 'Match transaction' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: transactionId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MatchPayload' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiMatch' + + /v1/businesses/{businessId}/bank-transactions/bulk-match: + post: + summary: 'Bulk match transactions' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SuggestedMatchesWithTransactions' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/bank-transactions/{transactionId}/ledger-entries: + get: + summary: 'List transaction ledger entries' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: transactionId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiLedgerEntry' + + /v1/businesses/{businessId}/payouts/{payoutId}: + get: + summary: 'Get payout from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: payoutId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiPayout' + + /v1/businesses/{businessId}/invoices/bulk: + post: + summary: 'Create invoices from batch' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/NewInvoicePostParams' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiInvoice' + + /v1/businesses/{businessId}/invoices/{invoiceId}: + get: + summary: 'Get invoice from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: invoiceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiInvoice' + + /v1/businesses/{businessId}/invoices/{invoiceId}/delete: + post: + summary: 'Delete invoice from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: invoiceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiInvoice' + + /v1/businesses/{businessId}/invoices/payments: + get: + summary: 'List invoice payments from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiInvoicePayment' + + /v1/businesses/{businessId}/invoices/payments/: + post: + summary: 'Create invoice payments from batch' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CreateStandaloneInvoicePayment' + + /v1/businesses/{businessId}/invoices/payments/{paymentId}: + get: + summary: 'Get invoice payment from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: paymentId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiInvoicePayment' + + /v1/businesses/{businessId}/invoices/payments/{paymentId}/delete: + post: + summary: 'Delete invoice payment from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: paymentId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiInvoicePayment' + + /v1/businesses/{businessId}/invoices/refunds/: + get: + summary: 'List refunds from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiRefund' + + /v1/businesses/{businessId}/external-accounts/: + get: + summary: 'List linked external accounts from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiExternalAccounts' + + /v1/businesses/{businessId}/external-accounts/{accountId}: + get: + summary: 'Get linked external account from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: accountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiExternalAccount' + + /v1/businesses/{businessId}/external-accounts/{accountId}/transactions: + get: + summary: 'Get transactions from linked external account' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: accountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiTransactions' + + /v1/businesses/{businessId}/external-accounts/{accountId}/balance-timestamps: + get: + summary: 'List balance timestamps from linked external account' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: accountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiBalanceTimestamps' + + /v1/businesses/{businessId}/external-accounts/{accountId}/opening-balance: + get: + summary: 'Get opening balance from linked external account' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: accountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiOpeningBalance' + + /v1/businesses/{businessId}/external-accounts/{accountId}/archive: + post: + summary: 'Archive linked external account' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: accountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/custom-accounts/: + post: + summary: 'Add custom account for business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewCustomAccountParams' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiCustomAccount' + get: + summary: 'List custom accounts from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiCustomAccounts' + + /v1/businesses/{businessId}/custom-accounts/{customAccountId}: + get: + summary: 'Get custom account from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: customAccountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiCustomAccount' + + patch: + summary: 'Update custom account from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: customAccountId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateCustomAccountParams' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiCustomAccount' + + put: + summary: 'Replace custom account from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: customAccountId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewCustomAccountParams' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiCustomAccount' + + post: + summary: 'Upload custom account transactions by JSON' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: customAccountId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewCustomTransactions' + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/custom-accounts/{customAccountId}/archive: + post: + summary: 'Archive custom account from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: customAccountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiCustomAccount' + + /v1/businesses/{businessId}/custom-accounts/{customAccountId}/reactivate: + post: + summary: 'Reactivate custom account from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: customAccountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiCustomAccount' + + /v1/businesses/{businessId}/custom-accounts/{customAccountId}/balance: + post: + summary: 'Create custom account balance timestamp' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: customAccountId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewCustomAccountBalance' + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/custom-accounts/{customAccountId}/opening-balance: + post: + summary: 'Calculate new custom account opening balance entry' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: customAccountId + in: path + required: true + schema: + type: string + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: '#/components/schemas/ApiOpeningBalance' + + /v1/businesses/{businessId}/ledger/balances: + get: + summary: 'List account balances from ledger' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiLedgerBalances' + + /v1/businesses/{businessId}/ledger/chart: + get: + summary: 'Get chart of accounts from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiChartOfAccounts' + + /v1/businesses/{businessId}/ledger/manual-entries: + post: + summary: 'Create manual ledger entries' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateManualLedgerEntry' + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: '#/components/schemas/ApiManualLedgerEntry' + + /v1/businesses/{businessId}/ledger/accounts: + get: + summary: 'Get ledger from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiLedger' + + post: + summary: 'Create ledger account for business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateApiLedgerAccount' + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: '#/components/schemas/SingleApiChartAccount' + + /v1/businesses/{businessId}/ledger/accounts/{accountId}/lines: + get: + summary: 'List ledger account line items' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: accountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiLedgerAccountLineItem' + + /v1/businesses/{businessId}/ledger/entries: + get: + summary: 'List ledger entries from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiLedgerEntry' + + /v1/businesses/{businessId}/ledger/entries/{entryId}: + get: + summary: 'Get ledger entry from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: entryId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiLedgerEntryWithTransaction' + + /v1/businesses/{businessId}/ledger/entries/{entryId}/reverse: + post: + summary: 'Create reversal entry for ledger entry' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: entryId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApiLedgerEntryWithTransaction' + + /v1/businesses/{businessId}/reports/profit-and-loss: + get: + summary: 'Get profit and loss report for business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ProfitAndLoss' + + /v1/businesses/{businessId}/reports/profit-and-loss-entries: + get: + summary: 'Get profit and loss entries from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PnlEntry' + + /v1/businesses/{businessId}/reports/download-transactions-csv: + get: + summary: 'Download ledger transactions from business as csv' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + text/csv: + schema: + type: string + + /v1/businesses/{businessId}/bank-transactions/tags: + post: + summary: 'Apply list of tags to list of transactions' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewTagsPostParams' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + + /v1/businesses/{businessId}/bank-transactions/{transactionId}/tags: + get: + summary: 'List tags applied to transaction' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: transactionId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + + /v1/businesses/{businessId}/bank-transactions/{transactionId}/tags/{tagId}: + delete: + summary: 'Remove tag from transaction' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: transactionId + in: path + required: true + schema: + type: string + - name: tagId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TagsDeleteParams' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/payouts/tags: + post: + summary: 'Apply list of tags to list of payouts' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewTagsPostParams' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + + delete: + summary: 'Delete list of payout tags' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TagsDeleteParams' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/payouts/{payoutId}/tags/: + get: + summary: 'List tags applied to payout' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: payoutId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + + /v1/businesses/{businessId}/invoices/tags: + post: + summary: 'Apply list of tags to list of invoices' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewTagsPostParams' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + + /v1/businesses/{businessId}/invoices/{invoiceId}/tags: + get: + summary: 'List tags applied to invoice' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: invoiceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + /v1/businesses/{businessId}/invoices/payments/{paymentId}/tags: + get: + summary: 'List tags applied to invoice payment' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: paymentId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + /v1/businesses/{businessId}/invoices/payments/{paymentId}/allocations/tags: + delete: + summary: 'Delete tags applied to payment allocation' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: paymentId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TagsDeleteParams' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/EmptyResponse' + + /v1/businesses/{businessId}/invoices/payments/allocations/tags: + post: + summary: 'Apply list of tags to list of invoice payment allocations' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NewTagsPostParams' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + + /v1/businesses/{businessId}/invoices/payments/{paymentId}/allocations/{allocationId}/tags: + get: + summary: 'List tags applied to invoice payment allocation' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: paymentId + in: path + required: true + schema: + type: string + - name: allocationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiTag' + + /v1/businesses/{businessId}/payouts/: + get: + summary: 'List payouts from business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApiPayout' + post: + summary: 'Create new payout for business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePayoutParams' + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: '#/components/schemas/ApiPayout' + + /v1/businesses/{businessId}/ledger/accounts/{accountId}: + get: + summary: 'Get or create ledger account on business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: accountId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SingleApiChartAccount' + put: + summary: 'Update ledger account on business' + parameters: + - name: businessId + in: path + required: true + schema: + type: string + - name: accountId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateApiLedgerAccount' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SingleApiChartAccount' + + +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + schemas: + RootInfoResponse: + type: object + properties: + clientName: + type: string + description: Name of the client + clientId: + type: string + format: uuid + description: Universally unique identifier of the client + required: + - clientName + - clientId + ApiChartOfAccounts: + type: object + properties: + accounts: + type: array + items: + $ref: '#/components/schemas/NestedApiChartAccount' + PlaidConfigurationResponse: + type: object + properties: + client_id: + type: string + secret_last_4: + type: string + UpdateCustomAccountParams: + type: object + properties: + external_id: + type: string + mask: + type: string + account_name: + type: string + institution_name: + type: string + account_type: + type: string + account_subtype: + type: string + NewTagsPostParams: + type: object + properties: + key_values: + type: array + items: + $ref: '#/components/schemas/TagKeyValue' + transaction_ids: + type: array + items: + type: string + format: uuid + ApiChartOfAccountsOld: + type: object + properties: + name: + type: string + accounts: + type: array + items: + $ref: '#/components/schemas/Account' + CreateManualLedgerEntry: + type: object + properties: + entry_at: + type: string + format: date-time + created_by: + type: string + memo: + type: string + line_items: + type: array + items: + $ref: '#/components/schemas/CreateLedgerEntryLineItem' + tags: + type: array + items: + $ref: '#/components/schemas/TagKeyValue' + CreateApiLedgerAccount: + type: object + properties: + name: + type: string + normality: + $ref: '#/components/schemas/BankTransactionDirection' + parent_id: + $ref: '#/components/schemas/AccountIdentifier' + stable_name: + $ref: '#/components/schemas/AccountStableName' + account_type: + $ref: '#/components/schemas/LedgerAccountType' + account_subtype: + $ref: '#/components/schemas/LedgerAccountSubtype' + MatchPayload: + oneOf: + - $ref: '#/components/schemas/ConfirmMatch' + CreateRefundParams: + type: object + properties: + external_id: + type: string + refunded_amount: + type: integer + fee: + type: integer + completed_at: + type: string + format: date-time + method: + $ref: '#/components/schemas/RefundPaymentMethod' + processor: + type: string + invoice_id: + type: string + format: uuid + invoice_external_id: + type: string + invoice_line_item_id: + type: string + format: uuid + invoice_payment_id: + type: string + format: uuid + invoice_payment_external_id: + type: string + recipient_name: + type: string + NewInvoicePostParams: + type: object + properties: + external_id: + type: string + sent_at: + type: string + format: date-time + due_at: + type: string + format: date-time + voided_at: + type: string + format: date-time + invoice_number: + type: string + recipient_name: + type: string + line_items: + type: array + items: + $ref: '#/components/schemas/NewInvoiceLineItem' + additional_sales_taxes: + type: array + items: + $ref: '#/components/schemas/ApiTaxLineItem' + additional_discount: + type: integer + tips: + type: integer + payments: + type: array + items: + $ref: '#/components/schemas/CreateImmediatePaymentInput' + ApiOpeningBalance: + type: object + properties: + external_account_external_id: + type: string + external_account_source: + $ref: '#/components/schemas/TransactionSource' + balance: + type: integer + effective_at: + type: string + format: date-time + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + NewBusinessParams: + type: object + properties: + external_id: + type: string + legal_name: + type: string + tin: + type: string + us_state: + $ref: '#/components/schemas/USState' + entity_type: + $ref: '#/components/schemas/BusinessType' + phone_number: + type: string + sms_enabled: + type: boolean + sms_categorization_start_date: + type: string + format: date-time + activation_at: + type: string + format: date-time + internal_bank_account_ids: + type: array + items: + $ref: '#/components/schemas/NewBusinessUnitAccountId' + unit_ids: + type: array + items: + $ref: '#/components/schemas/NewBusinessUnitAccountId' + plaid_items: + type: array + items: + $ref: '#/components/schemas/CreatePlaidItem' + skip_onboarding: + type: boolean + industry: + $ref: '#/components/schemas/BusinessIndustry' + stripe_connect_accounts: + type: array + items: + $ref: '#/components/schemas/StripeConnectAccountInput' + StripeConfigurationResponse: + type: object + properties: + stripe_secret_last_4: + type: string + NewCustomAccountBalance: + type: object + properties: + amount: + type: integer + date: + type: string + format: date-time + UpdateApiLedgerAccount: + type: object + properties: + stable_name: + $ref: '#/components/schemas/AccountStableName' + name: + type: string + normality: + $ref: '#/components/schemas/BankTransactionDirection' + parent_id: + $ref: '#/components/schemas/AccountIdentifier' + account_type: + $ref: '#/components/schemas/LedgerAccountType' + account_subtype: + $ref: '#/components/schemas/LedgerAccountSubtype' + ApiInvoice: + type: object + properties: + id: + type: string + format: uuid + business_id: + type: string + format: uuid + external_id: + type: string + status: + $ref: '#/components/schemas/InvoiceStatus' + sent_at: + type: string + format: date-time + due_at: + type: string + format: date-time + paid_at: + type: string + format: date-time + voided_at: + type: string + format: date-time + invoice_number: + type: string + recipient_name: + type: string + line_items: + type: array + items: + $ref: '#/components/schemas/ApiInvoiceLineItem' + subtotal: + type: integer + additional_discount: + type: integer + additional_sales_taxes_total: + type: integer + additional_sales_taxes: + type: array + items: + $ref: '#/components/schemas/ApiTaxLineItem' + tips: + type: integer + total_amount: + type: integer + outstanding_balance: + type: integer + payment_allocations: + type: array + items: + $ref: '#/components/schemas/ApiInvoicePaymentAllocation' + imported_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + transaction_tags: + type: array + items: + $ref: '#/components/schemas/ApiTag' + ApiBalanceTimestamps: + type: object + properties: + external_account_external_id: + type: string + external_account_source: + $ref: '#/components/schemas/TransactionSource' + balance_timestamps: + type: array + items: + $ref: '#/components/schemas/ApiBalanceTimestamp' + ApiLedgerBalances: + type: object + properties: + accounts: + type: array + items: + $ref: '#/components/schemas/NestedApiLedgerAccount' + BusinessType: + type: string + enum: + - SOLE_PROP + - C_CORP + - LLC + - S_CORP + - PARTNERSHIP + USState: + type: string + enum: + - ALABAMA + - ALASKA + - ARIZONA + - ARKANSAS + - CALIFORNIA + - COLORADO + - CONNECTICUT + - DELAWARE + - DISTRICT_OF_COLUMBIA + - FLORIDA + - GEORGIA + - HAWAII + - IDAHO + - ILLINOIS + - INDIANA + - IOWA + - KANSAS + - KENTUCKY + - LOUISIANA + - MAINE + - MARYLAND + - MASSACHUSETTS + - MICHIGAN + - MINNESOTA + - MISSISSIPPI + - MISSOURI + - MONTANA + - NEBRASKA + - NEVADA + - NEW_HAMPSHIRE + - NEW_JERSEY + - NEW_MEXICO + - NEW_YORK + - NORTH_CAROLINA + - NORTH_DAKOTA + - OHIO + - OKLAHOMA + - OREGON + - PENNSYLVANIA + - RHODE_ISLAND + - SOUTH_CAROLINA + - SOUTH_DAKOTA + - TENNESSEE + - TEXAS + - UTAH + - VERMONT + - VIRGINIA + - WASHINGTON + - WEST_VIRGINIA + - WISCONSIN + - WYOMING + - PUERTO_RICO + BusinessIndustry: + type: string + enum: + - TRUCKING + - DEMO_TRADES + - MEDSPA + - FLORIST + ApiTag: + type: object + properties: + id: + type: string + format: uuid + key: + type: string + value: + type: string + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + deleted_at: + type: string + format: date-time + nullable: true + UpdateModeLinkCreationPayload: + type: object + properties: + plaid_item_id: + type: string + ApiBusiness: + type: object + properties: + id: + type: string + format: uuid + external_id: + type: string + legal_name: + type: string + tin: + type: string + us_state: + $ref: '#/components/schemas/USState' + entity_type: + $ref: '#/components/schemas/BusinessType' + industry: + $ref: '#/components/schemas/BusinessIndustry' + phone_number: + type: string + sms_enabled: + type: boolean + sms_categorization_start_date: + type: string + format: date-time + activation_at: + type: string + format: date-time + imported_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + archived_at: + type: string + format: date-time + unit_accounts: + type: array + items: + $ref: '#/components/schemas/APIUnitAccount' + plaid_items: + type: array + items: + $ref: '#/components/schemas/ApiPlaidItem' + previously_imported: + type: boolean + stripe_connect_accounts: + type: array + items: + $ref: '#/components/schemas/ApiStripeConnectAccount' + ApiMatch: + type: object + properties: + id: + type: string + format: uuid + match_type: + $ref: '#/components/schemas/MatchType' + bank_transaction: + $ref: '#/components/schemas/ApiBankTransactionInterface' + details: + $ref: '#/components/schemas/ApiMatchDetails' + BusinessActivitySummary: + type: object + properties: + businessId: + type: string + format: uuid + legal_name: + type: string + sms_sent: + type: integer + sms_received: + type: integer + sms_sent_last_30: + type: integer + sms_received_last_30: + type: integer + categorized_last_month: + type: integer + uncategorized_last_month: + type: integer + categorized_by_api_last_month: + type: integer + categorized_by_sms_last_month: + type: integer + categorized_pct_last_month: + type: number + categorized_this_month: + type: integer + uncategorized_this_month: + type: integer + categorized_by_api_this_month: + type: integer + categorized_by_sms_this_month: + type: integer + categorized_pct_this_month: + type: number + days_since_last_activity: + type: integer + current_sms_queue_length: + type: integer + EmptyResponse: + type: object + NewCustomTransactions: + type: object + properties: + new_custom_transaction_params: + type: array + items: + $ref: '#/components/schemas/NewCustomTransactionParams' + ClientActivitySummary: + type: object + properties: + businesses_onboarded: + type: integer + businesses_sent_sms: + type: integer + businesses_engaged: + type: integer + businesses_engaged_last_30: + type: integer + new_businesses_engaged_last_30: + type: integer + businesses_engaged_last_90: + type: integer + new_businesses_engaged_last_90: + type: integer + categorized_by_sms_last_30: + type: integer + categorized_by_api_last_30: + type: integer + ApiCustomAccounts: + type: object + properties: + custom_accounts: + type: array + items: + $ref: '#/components/schemas/ApiCustomAccount' + PnlEntry: + anyOf: + - $ref: '#/components/schemas/BankTransactionPnlEntry' + - $ref: '#/components/schemas/SplitBankTransactionPnlEntry' + ExternalAccountQueryParams: + type: object + properties: + account_source: + $ref: '#/components/schemas/TransactionSource' + start_date: + type: string + format: date-time + end_date: + type: string + format: date-time + + + + TagsDeleteParams: + type: object + properties: + tag_ids: + type: array + items: + type: string + format: uuid + description: List of tag IDs to delete + + ApiLinkToken: + type: object + properties: + link_token: + type: string + description: The link token to use for Plaid authentication + + ApiPlaidAccount: + type: object + properties: + id: + type: string + format: uuid + description: Universally unique identifier of the Plaid account + plaid_account_id: + type: string + description: Plaid's unique identifier for the account + plaid_item_id: + type: string + description: Plaid's unique identifier for the item + status: + $ref: '#/components/schemas/AccountStatus' + mask: + type: string + nullable: true + description: Last 4 digits of the account number + name: + type: string + nullable: true + description: Name of the account + account_type: + type: string + nullable: true + description: Type of the account + subtype: + type: string + nullable: true + description: Subtype of the account + created_at: + type: string + format: date-time + description: Timestamp when the account was created + updated_at: + type: string + format: date-time + description: Timestamp when the account was last updated + PlaidItemConfiguration: + type: object + properties: + sync_transactions: + type: boolean + description: Whether to sync transactions for this item + current_cursor: + type: string + nullable: true + description: The current cursor position for transaction sync + archived: + type: boolean + description: Whether this item configuration is archived + + PlaidInstitution: + type: object + properties: + name: + type: string + description: The name of the financial institution + institution_id: + type: string + description: Plaid's unique identifier for the institution + + PlaidLinkPublicToken: + type: object + properties: + public_token: + type: string + description: The public token returned by Plaid Link + institution: + $ref: '#/components/schemas/PlaidInstitution' + + PlaidAccountConfiguration: + type: object + properties: + sync_from_time: + type: string + format: date-time + nullable: true + description: Timestamp to start syncing account transactions from + status: + $ref: '#/components/schemas/AccountStatus' + + CreatePlaidItem: + type: object + properties: + item_id: + type: string + description: Plaid's unique identifier for the item + access_token: + type: string + description: Access token for the Plaid item + sync_transactions: + type: boolean + default: true + description: Whether to sync transactions for this item + institution_plaid_id: + type: string + nullable: true + description: Plaid's identifier for the institution + + ApiPlaidInstitution: + type: object + properties: + id: + type: string + description: Plaid's unique identifier for the institution + name: + type: string + description: The name of the financial institution + logo: + type: string + nullable: true + description: URL for the institution's logo + + + ApiTransactions: + type: object + properties: + external_account_external_id: + type: string + external_account_source: + $ref: '#/components/schemas/TransactionSource' + transactions: + type: array + items: + $ref: '#/components/schemas/ApiBankTransactionDataOnly' + AccountStatus: + type: string + enum: + - ACTIVE + - ARCHIVED + ApiInvoicePayment: + type: object + properties: + id: + type: string + format: uuid + external_id: + type: string + nullable: true + at: + type: string + format: date-time + method: + $ref: '#/components/schemas/PaymentMethod' + fee: + type: integer + format: int64 + amount: + type: integer + format: int64 + processor: + type: string + nullable: true + imported_at: + type: string + format: date-time + allocations: + type: array + items: + $ref: '#/components/schemas/ApiInvoicePaymentAllocation' + transaction_tags: + type: array + items: + $ref: '#/components/schemas/ApiTag' + ApiBankTransaction: + type: object + properties: + id: + type: string + format: uuid + description: Universally unique identifier of the bank transaction + business_id: + type: string + format: uuid + description: Universally unique identifier of the business + source: + $ref: '#/components/schemas/TransactionSource' + source_transaction_id: + type: string + description: Transaction ID from the source + source_account_id: + type: string + description: Account ID from the source + imported_at: + type: string + format: date-time + description: Timestamp when the transaction was imported + date: + type: string + format: date-time + description: Date of the transaction + direction: + $ref: '#/components/schemas/BankTransactionDirection' + amount: + type: integer + format: int64 + description: Transaction amount in cents + counterparty_name: + type: string + nullable: true + description: Name of the transaction counterparty + description: + type: string + nullable: true + description: Description of the transaction + account_name: + type: string + nullable: true + description: Name of the bank account + categorization_status: + $ref: '#/components/schemas/CategorizationStatus' + category: + $ref: '#/components/schemas/ApiCategorization' + categorization_method: + $ref: '#/components/schemas/ClassifierAgent' + categorization_flow: + $ref: '#/components/schemas/ApiCategorizationFlow' + categorization_ux: + $ref: '#/components/schemas/CategorizationUX' + projected_income_category: + $ref: '#/components/schemas/ProjectedIncomeCategory' + suggested_matches: + type: array + items: + $ref: '#/components/schemas/ApiSuggestedMatch' + match: + $ref: '#/components/schemas/ApiMatch' + transaction_tags: + type: array + items: + $ref: '#/components/schemas/ApiTag' + ApiManualLedgerEntry: + type: object + properties: + id: + type: string + format: uuid + description: Universally unique identifier of the manual ledger entry + created_by: + type: string + description: Name of the user who created the manual ledger entry + memo: + type: string + description: Memo or description for the manual ledger entry + entry_id: + type: string + description: ID of the associated ledger entry + ApiExternalAccount: + type: object + properties: + id: + type: string + format: uuid + description: Universally unique identifier of the external account + external_account_external_id: + type: string + description: External ID of the external account + external_account_source: + $ref: '#/components/schemas/TransactionSource' + external_account_name: + type: string + description: Name of the external account + latest_balance_timestamp: + $ref: '#/components/schemas/ApiBalanceTimestamp' + current_ledger_balance: + type: integer + format: int64 + description: Current ledger balance of the external account in cents + mask: + type: string + nullable: true + description: Masked account number of the external account + institution: + $ref: '#/components/schemas/ApiExternalAccountInstitution' + connection_needs_repair_as_of: + type: string + format: date-time + nullable: true + description: Timestamp indicating when the connection started needing repair + SingleApiChartAccount: + type: object + properties: + id: + $ref: '#/components/schemas/AccountIdentifier' + name: + type: string + description: Name of the account + stable_name: + $ref: '#/components/schemas/AccountStableName' + normality: + $ref: '#/components/schemas/BankTransactionDirection' + account_type: + $ref: '#/components/schemas/ApiLedgerAccountType' + account_subtype: + $ref: '#/components/schemas/ApiLedgerAccountSubtype' + ApiCustomAccount: + type: object + properties: + id: + type: string + format: uuid + external_id: + type: string + mask: + type: string + nullable: true + account_name: + type: string + institution_name: + type: string + account_type: + type: string + nullable: true + account_subtype: + type: string + nullable: true + created_at: + type: string + format: date-time + nullable: true + updated_at: + type: string + format: date-time + nullable: true + archived_at: + type: string + format: date-time + nullable: true + ApiLedgerEntryWithTransaction: + type: object + properties: + id: + type: string + description: Unique identifier of the ledger entry + business_id: + type: string + format: uuid + ledger_id: + type: string + agent: + oneOf: + - $ref: '#/components/schemas/ClassifierAgent' + entry_type: + oneOf: + - $ref: '#/components/schemas/LedgerEntryType' + date: + type: string + format: date-time + entry_at: + type: string + format: date-time + reversal_of_id: + type: string + nullable: true + reversal_id: + type: string + nullable: true + line_items: + type: array + items: + $ref: '#/components/schemas/ApiLineItem' + transaction: + oneOf: + - $ref: '#/components/schemas/ApiBankTransaction' + invoice: + oneOf: + - $ref: '#/components/schemas/ApiInvoice' + manual_entry: + oneOf: + - $ref: '#/components/schemas/ApiManualLedgerEntry' + source: + oneOf: + - $ref: '#/components/schemas/LedgerEntrySource' + ApiLineItem: + type: object + properties: + id: + type: string + entry_id: + type: string + account: + $ref: '#/components/schemas/SingleApiChartAccount' + amount: + type: integer + format: int64 + direction: + $ref: '#/components/schemas/BankTransactionDirection' + entry_at: + type: string + format: date-time + createdAt: + type: string + format: date-time + CreatePayoutParams: + type: object + properties: + external_id: + type: string + paid_out_amount: + type: integer + format: int64 + fee: + type: integer + format: int64 + refunds_amount: + type: integer + format: int64 + processor: + $ref: '#/components/schemas/PaymentProcessor' + completed_at: + type: string + format: date-time + payments: + type: array + items: + $ref: '#/components/schemas/PayoutPaymentInput' + overwrite_payment_fees: + type: boolean + nullable: true + PayoutPaymentInput: + type: object + properties: + invoice_payment_external_id: + type: string + amount: + type: integer + format: int64 + ApiPayout: + type: object + properties: + id: + type: string + format: uuid + external_id: + type: string + nullable: true + business_id: + type: string + format: uuid + paid_out_amount: + type: integer + format: int64 + fee: + type: integer + format: int64 + processor: + $ref: '#/components/schemas/PaymentProcessor' + imported_at: + type: string + format: date-time + completed_at: + type: string + format: date-time + match: + oneOf: + - $ref: '#/components/schemas/ApiMatch' + payments: + type: array + items: + $ref: '#/components/schemas/ApiInvoicePayment' + transaction_tags: + type: array + items: + $ref: '#/components/schemas/ApiTag' + ApiExternalAccounts: + type: object + properties: + external_accounts: + type: array + items: + $ref: '#/components/schemas/ApiExternalAccount' + ApiLedger: + type: object + properties: + accounts: + type: array + items: + $ref: '#/components/schemas/NestedApiLedgerAccount' + entries: + type: array + items: + $ref: '#/components/schemas/ApiLedgerEntry' + PlaidConfigurationInput: + type: object + properties: + client_id: + type: string + secret: + type: string + ApiLedgerEntry: + type: object + properties: + id: + type: string + business_id: + type: string + format: uuid + ledger_id: + type: string + agent: + oneOf: + - $ref: '#/components/schemas/ClassifierAgent' + entry_type: + oneOf: + - $ref: '#/components/schemas/LedgerEntryType' + date: + type: string + format: date-time + entry_at: + type: string + format: date-time + reversal_of_id: + type: string + nullable: true + reversal_id: + type: string + nullable: true + line_items: + type: array + items: + $ref: '#/components/schemas/ApiLineItem' + + NestedApiChartAccount: + type: object + properties: + id: + $ref: '#/components/schemas/AccountIdentifier' + name: + type: string + stable_name: + oneOf: + - $ref: '#/components/schemas/AccountStableName' + normality: + $ref: '#/components/schemas/BankTransactionDirection' + account_type: + oneOf: + - $ref: '#/components/schemas/ApiLedgerAccountType' + account_subtype: + oneOf: + - $ref: '#/components/schemas/ApiLedgerAccountSubtype' + sub_accounts: + type: array + items: + $ref: '#/components/schemas/NestedApiChartAccount' + CreateLedgerEntryLineItem: + type: object + properties: + account_identifier: + $ref: '#/components/schemas/AccountIdentifier' + amount: + type: integer + format: int64 + direction: + $ref: '#/components/schemas/BankTransactionDirection' + TagKeyValue: + type: object + properties: + key: + type: string + value: + type: string + NewInvoiceLineItem: + type: object + properties: + account_identifier: + oneOf: + - $ref: '#/components/schemas/AccountIdentifier' + description: + type: string + nullable: true + product: + type: string + unit_price: + type: integer + format: int64 + quantity: + type: number + format: bigdecimal + nullable: true + discount_amount: + type: integer + format: int64 + nullable: true + sales_taxes: + type: array + items: + $ref: '#/components/schemas/ApiTaxLineItem' + nullable: true + is_prepayment: + type: boolean + nullable: true + CreateImmediatePaymentInput: + type: object + properties: + external_id: + type: string + nullable: true + method: + $ref: '#/components/schemas/PaymentMethod' + fee: + type: integer + format: int64 + nullable: true + amount: + type: integer + format: int64 + processor: + type: string + nullable: true + ApiTaxLineItem: + type: object + properties: + tax_account: + oneOf: + - $ref: '#/components/schemas/TaxAccountIdentifier' + amount: + type: integer + format: int64 + NewBusinessUnitAccountId: + type: object + properties: + unit_id: + type: string + account_name: + type: string + nullable: true + opening_balance_init_to_zero: + type: boolean + opening_balance_do_not_init: + type: boolean + StripeConnectAccountInput: + type: object + properties: + stripe_id: + type: string + TransactionSource: + type: string + enum: + - UNIT + - PLAID + - API + - STRIPE + - CUSTOM + ApiStripeConnectAccount: + type: object + properties: + stripe_connect_account_stripe_id: + type: string + last_synced_at: + type: string + format: date-time + nullable: true + archived_at: + type: string + format: date-time + nullable: true + created_at: + type: string + format: date-time + created_in_stripe: + type: string + format: date-time + merchant_name: + type: string + nullable: true + MatchType: + type: string + enum: + - TRANSFER + - INVOICE_PAYMENT + - PAYOUT + ApiBankTransactionInterface: + type: object + properties: + id: + type: string + format: uuid + business_id: + type: string + format: uuid + source: + $ref: '#/components/schemas/TransactionSource' + source_transaction_id: + type: string + source_account_id: + type: string + imported_at: + type: string + format: date-time + date: + type: string + format: date-time + direction: + $ref: '#/components/schemas/BankTransactionDirection' + amount: + type: integer + format: int64 + counterparty_name: + type: string + nullable: true + description: + type: string + nullable: true + account_name: + type: string + nullable: true + categorization_status: + $ref: '#/components/schemas/CategorizationStatus' + ApiMatchDetails: + type: object + required: + - id + - amount + - date + properties: + id: + type: string + format: uuid + amount: + type: integer + format: int64 + date: + type: string + format: date-time + description: + type: string + nullable: true + InvoiceStatus: + type: string + enum: + - SENT + - PARTIALLY_PAID + - PAID + - VOIDED + ApiInvoiceLineItem: + type: object + properties: + id: + type: string + format: uuid + invoice_id: + type: string + format: uuid + account_identifier: + oneOf: + - $ref: '#/components/schemas/AccountIdentifier' + description: + type: string + nullable: true + product: + type: string + nullable: true + unit_price: + $ref: '#/components/schemas/SignedAmount' + quantity: + type: number + format: bigdecimal + subtotal: + type: integer + format: int64 + discount_amount: + type: integer + format: int64 + sales_taxes_total: + type: integer + format: int64 + sales_taxes: + type: array + items: + $ref: '#/components/schemas/ApiTaxLineItem' + nullable: true + total_amount: + type: integer + format: int64 + ApiInvoicePaymentAllocation: + type: object + properties: + invoice_id: + type: string + format: uuid + payment_id: + type: string + format: uuid + amount: + type: integer + format: int64 + transaction_tags: + type: array + items: + $ref: '#/components/schemas/ApiTag' + PaymentMethod: + type: string + enum: + - CASH + - CHECK + - CREDIT_CARD + - ACH + - REDEEMED_PREPAYMENT + - OTHER + + PaymentProcessor: + type: string + enum: + - STRIPE + - PAYPAL + + PaymentBalanceTransactionType: + type: string + enum: + - charge + - payment + - refund + - payment_refund + + + + ClassifierAgent: + type: string + enum: + - SMS + - API + - LAYER_AUTO + - LAYER_MANUAL + + ProjectedIncomeCategory: + type: string + enum: + - REVENUE + - EXPENSE + - EXCLUDE + + LayerApiSortOrder: + type: string + enum: + - ASC + - ASCENDING + - DES + - DESC + - DESCENDING + + ReportingBasis: + type: string + enum: + - ACCRUAL + - CASH + + BusinessLedgerProvider: + type: string + enum: + - MODERN_TREASURY + - LOCAL + - SPLITS_ONLY + + BusinessLedgerStatus: + type: string + enum: + - CREATING + - ACTIVE + - CREATION_FAILED + - MIGRATING + + ApiErrorType: + type: string + enum: + - ResourceArchived + - AuthFailure + - Plaid + - Stripe + - InvalidState + - ResourceNotFound + - InvalidParameters + - JsonSerialization + - Unknown + - BadRequest + - PaginationCursor + - Conflict + CurrencyCode: + type: string + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYR + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KMF + - KPW + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRO + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLL + - SOS + - SPL + - SRD + - STD + - SVC + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VEF + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWD + PnlTemplates: + type: string + enum: + - DEFAULT + - TRUCKING + - MEDSPA + - MEDSPA_NO_LICENSING + - CITRUS + - CITRUS_NO_LICENSING + - FLORIST + + + + ClientStringName: + type: string + enum: + - CAUGHT_UP_LINK_PROMPT + - CAUGHT_UP_ONBOARDING_LINK_PROMPT + - ONBOARDING_COMPLETE_NOT_CAUGHT_UP + - CATEGORIZE_IN_APP_PROMPT + + FallbackChildHandling: + type: string + enum: + - LINE_ITEM + - OTHER_BUCKET + - OMIT + - THROW_ERROR + BusinessUpdatePayload: + type: object + properties: + legalName: + type: string + nullable: true + tin: + type: string + nullable: true + usState: + nullable: true + allOf: + - $ref: '#/components/schemas/USState' + entityType: + nullable: true + allOf: + - $ref: '#/components/schemas/BusinessType' + industry: + nullable: true + allOf: + - $ref: '#/components/schemas/BusinessIndustry' + phoneNumber: + type: string + nullable: true + smsEnabled: + type: boolean + nullable: true + smsCategorizationStartDate: + type: string + format: date-time + nullable: true + activationAt: + type: string + format: date-time + nullable: true + archivedAt: + type: string + format: date-time + nullable: true + unitIds: + type: array + items: + $ref: '#/components/schemas/NewBusinessUnitAccountId' + nullable: true + internalBankAccountIds: + type: array + items: + $ref: '#/components/schemas/NewBusinessUnitAccountId' + nullable: true + plaidItems: + type: array + items: + $ref: '#/components/schemas/CreatePlaidItem' + nullable: true + stripeConnectAccounts: + type: array + items: + $ref: '#/components/schemas/StripeConnectAccountInput' + nullable: true + ApiExternalAccountInstitution: + type: object + properties: + name: + type: string + logo: + type: string + nullable: true + ApiSuggestedMatch: + type: object + properties: + id: + type: string + format: uuid + matchType: + $ref: '#/components/schemas/MatchType' + details: + $ref: '#/components/schemas/ApiMatchDetails' + CategorizationUX: + type: object + properties: + prompt: + type: string + nullable: true + options: + type: array + items: + $ref: '#/components/schemas/CategorizationOption' + + CategorizationOption: + oneOf: + - $ref: '#/components/schemas/CategorizationOption_MenuOption' + - $ref: '#/components/schemas/CategorizationOption_CategorizeOption' + - $ref: '#/components/schemas/CategorizationOption_SplitOption' + - $ref: '#/components/schemas/CategorizationOption_FreeSelectOption' + + CategorizationOption_MenuOption: + type: object + properties: + name: + type: string + display_string: + type: string + tooltip: + type: string + nullable: true + menu_options: + type: array + items: + $ref: '#/components/schemas/CategorizationOption' + + CategorizationOption_CategorizeOption: + type: object + properties: + name: + type: string + display_string: + type: string + tooltip: + type: string + nullable: true + account: + $ref: '#/components/schemas/ApiCategorization' + + CategorizationOption_SplitOption: + type: object + properties: + name: + type: string + default: SPLIT + display_string: + type: string + default: Split + tooltip: + type: string + nullable: true + + CategorizationOption_FreeSelectOption: + type: object + properties: + name: + type: string + default: FREE_SELECT + display_string: + type: string + default: Something else + tooltip: + type: string + nullable: true + ApiCategorizationFlow: + type: object + properties: + type: + $ref: '#/components/schemas/InputStrategy' + category: + oneOf: + - $ref: '#/components/schemas/ApiCategorization' + suggestions: + type: array + items: + $ref: '#/components/schemas/ApiCategorization' + nullable: true + CategorizationStatus: + type: string + enum: + - UNCATEGORIZED + - AUTO_CATEGORIZED + - CATEGORIZED + - DELETED + - TRANSFER + - REFUND + - INVOICE_PAYMENT + StripeConfigurationInput: + type: object + properties: + secret: + type: string + NewCustomAccountParams: + type: object + properties: + external_id: + type: string + mask: + type: string + account_name: + type: string + institution_name: + type: string + account_type: + type: string + account_subtype: + type: string + CreateStandaloneInvoicePayment: + type: object + properties: + external_id: + type: string + nullable: true + paid_at: + type: string + format: date-time + method: + $ref: '#/components/schemas/PaymentMethod' + fee: + type: integer + format: int64 + nullable: true + amount: + type: integer + format: int64 + processor: + type: string + nullable: true + invoice_payments: + type: array + items: + $ref: '#/components/schemas/InvoicePaymentAllocationInput' + InvoicePaymentAllocationInput: + type: object + properties: + invoice_id: + type: string + format: uuid + amount: + type: integer + format: int64 + ApiRefund: + type: object + properties: + id: + type: string + format: uuid + external_id: + type: string + nullable: true + refunded_amount: + type: integer + format: int64 + fee: + type: integer + format: int64 + completed_at: + type: string + format: date-time + method: + $ref: '#/components/schemas/RefundPaymentMethod' + processor: + type: string + nullable: true + invoice_id: + type: string + format: uuid + nullable: true + invoice_line_item_id: + type: string + format: uuid + nullable: true + invoice_payment_id: + type: string + format: uuid + nullable: true + recipient_name: + type: string + ApiLedgerAccountLineItem: + type: object + properties: + id: + type: string + entry_id: + type: string + account: + $ref: '#/components/schemas/SingleApiChartAccount' + amount: + type: integer + format: int64 + direction: + $ref: '#/components/schemas/BankTransactionDirection' + date: + type: string + format: date-time + source: + $ref: '#/components/schemas/LedgerEntrySource' + running_balance: + type: integer + format: int64 + transaction: + $ref: '#/components/schemas/ApiBankTransaction' + invoice: + $ref: '#/components/schemas/ApiInvoice' + ProfitAndLoss: + type: object + properties: + business_id: + type: string + format: uuid + start_date: + type: string + format: date-time + end_date: + type: string + format: date-time + income: + $ref: '#/components/schemas/LineItem' + cost_of_goods_sold: + oneOf: + - $ref: '#/components/schemas/LineItem' + gross_profit: + type: integer + format: int64 + expenses: + $ref: '#/components/schemas/LineItem' + profit_before_taxes: + type: integer + format: int64 + taxes: + $ref: '#/components/schemas/LineItem' + net_profit: + type: integer + format: int64 + other_outflows: + oneOf: + - $ref: '#/components/schemas/LineItem' + nullable: true + personal_expenses: + oneOf: + - $ref: '#/components/schemas/LineItem' + fully_categorized: + type: boolean + SuggestedMatchesWithTransactions: + type: object + properties: + match_pairs: + type: array + items: + $ref: '#/components/schemas/SuggestedMatchWithTransaction' + SuggestedMatchWithTransaction: + type: object + properties: + transaction_id: + type: string + format: uuid + suggested_match_id: + type: string + format: uuid + ApiPlaidItem: + type: object + properties: + item_id: + type: string + access_token: + type: string + description: Access token for the Plaid item + sync_transactions: + type: boolean + description: Whether to sync transactions for this item + institution: + oneOf: + - $ref: '#/components/schemas/ApiPlaidInstitution' + description: The financial institution associated with this item (may be null for older items) + LedgerEntryType: + type: string + enum: + - BANK + - MANUAL + - OPENING_BALANCE + ApiLedgerAccountType: + type: object + properties: + value: + $ref: '#/components/schemas/LedgerAccountType' + display_name: + type: string + + LedgerAccountType: + type: string + enum: + - ASSET + - LIABILITY + - EQUITY + - REVENUE + - COGS + - EXPENSE + + ApiLedgerAccountSubtype: + type: object + properties: + value: + $ref: '#/components/schemas/LedgerAccountSubtype' + display_name: + type: string + + LedgerAccountSubtype: + type: string + enum: + - CURRENT + - FIXED + - ACCOUNTS_RECEIVABLE + - LONG_TERM + - ACCOUNTS_PAYABLE + - CREDIT_CARD + - OWNERS_EQUITY + - PRODUCT_INCOME + - SERVICE_INCOME + - DIRECT_LABOR + - MATERIAL + - OVERHEAD + - ADVERTISING_AND_PROMOTION + - AUTOMOTIVE_EXPENSE + - BANK_FEES + - CHARITABLE_CONTRIBUTIONS + - COMMISSIONS_AND_FEES + - DUES_AND_SUBSCRIPTIONS + - INSURANCE + - INTEREST + - LEGAL_AND_PROFESSIONAL_FEES + - LICENSES + - MEALS_AND_ENTERTAINMENT + - OFFICE_SUPPLIES + - PAYROLL_ADMIN + - PAYROLL_BENEFITS + - PAYROLL_TAXES + - PHONE_AND_INTERNET + - RENT_OR_LEASE + - REPAIRS_AND_MAINTENANCE + - TAXES_AND_LICENSES + - TRAVEL + - UTILITIES + - DEPRECIATION + CategoryList: + type: object + properties: + categories: + type: array + items: + $ref: '#/components/schemas/NestedApiCategorization' + AccountIdentifier: + oneOf: + - $ref: '#/components/schemas/AccountId' + - $ref: '#/components/schemas/AccountStableName' + + AccountId: + type: object + properties: + id: + type: string + AccountStableName: + type: object + properties: + name: + type: string + BankTransactionPnlEntry: + type: object + properties: + id: + type: string + format: uuid + business_id: + type: string + format: uuid + source: + $ref: '#/components/schemas/TransactionSource' + source_transaction_id: + type: string + source_account_id: + type: string + imported_at: + type: string + format: date-time + date: + type: string + format: date-time + direction: + $ref: '#/components/schemas/BankTransactionDirection' + amount: + type: integer + format: int64 + counterparty_name: + type: string + nullable: true + description: + type: string + nullable: true + account_name: + type: string + nullable: true + categorization_status: + $ref: '#/components/schemas/CategorizationStatus' + category: + $ref: '#/components/schemas/ApiCategorization' + categorization_method: + oneOf: + - $ref: '#/components/schemas/ClassifierAgent' + categorization_flow: + oneOf: + - $ref: '#/components/schemas/ApiCategorizationFlow' + categorization_ux: + oneOf: + - $ref: '#/components/schemas/CategorizationUX' + projected_income_category: + $ref: '#/components/schemas/ProjectedIncomeCategory' + + SplitBankTransactionPnlEntry: + type: object + properties: + id: + type: string + format: uuid + business_id: + type: string + format: uuid + source: + $ref: '#/components/schemas/TransactionSource' + source_transaction_id: + type: string + source_account_id: + type: string + imported_at: + type: string + format: date-time + date: + type: string + format: date-time + direction: + $ref: '#/components/schemas/BankTransactionDirection' + amount: + type: integer + format: int64 + counterparty_name: + type: string + nullable: true + description: + type: string + nullable: true + account_name: + type: string + nullable: true + categorization_status: + $ref: '#/components/schemas/CategorizationStatus' + category: + $ref: '#/components/schemas/ApiCategorization' + categorization_method: + oneOf: + - $ref: '#/components/schemas/ClassifierAgent' + categorization_flow: + oneOf: + - $ref: '#/components/schemas/ApiCategorizationFlow' + categorization_ux: + oneOf: + - $ref: '#/components/schemas/CategorizationUX' + projected_income_category: + $ref: '#/components/schemas/ProjectedIncomeCategory' + NewCustomTransactionParams: + type: object + properties: + external_id: + type: string + amount: + type: integer + format: int64 + direction: + $ref: '#/components/schemas/BankTransactionDirection' + date: + type: string + format: date-time + merchant_name: + type: string + nullable: true + merchant_category_code: + type: string + nullable: true + currency_code: + $ref: '#/components/schemas/CurrencyCode' + description: + type: string + display_description: + type: string + nullable: true + balance: + type: integer + format: int64 + nullable: true + opening_balance_do_not_init: + type: boolean + opening_balance_init_to_zero: + type: boolean + init_external_transaction_mappings: + type: boolean + InputStrategy: + type: string + enum: + - CATEGORY_SELECT + - SIMPLE_SEARCH + - UNCATEGORIZED + ApiBankTransactionDataOnly: + type: object + properties: + id: + type: string + format: uuid + business_id: + type: string + format: uuid + source: + $ref: '#/components/schemas/TransactionSource' + source_transaction_id: + type: string + source_account_id: + type: string + imported_at: + type: string + format: date-time + date: + type: string + format: date-time + direction: + $ref: '#/components/schemas/BankTransactionDirection' + amount: + type: integer + format: int64 + counterparty_name: + type: string + nullable: true + description: + type: string + nullable: true + account_name: + type: string + nullable: true + categorization_status: + $ref: '#/components/schemas/CategorizationStatus' + SignedAmount: + type: integer + format: int64 + ExclusionClassification: + oneOf: + - $ref: '#/components/schemas/Exclusion' + discriminator: + propertyName: classificationType + mapping: + Exclusion: '#/components/schemas/Exclusion' + Exclusion: + type: object + properties: + exclusion_type: + $ref: '#/components/schemas/ExclusionType' + required: + - exclusion_type + ExclusionType: + type: string + description: "Type of exclusion for a bank transaction." + enum: + - PERSONAL_EXPENSES + - PERSONAL_INFLOWS + - OTHER_EXCLUSION + x-displayStrings: + PERSONAL_EXPENSES: "Personal transactions" + PERSONAL_INFLOWS: "Personal income sources" + OTHER_EXCLUSION: "Other exclusion" + ApiCategorization: + description: Base schema for categorization of API entities. + oneOf: + - $ref: '#/components/schemas/AccountCategorization' + - $ref: '#/components/schemas/ExclusionCategorization' + - $ref: '#/components/schemas/SplitCategorization' + - $ref: '#/components/schemas/NestedApiCategorization' + discriminator: + propertyName: type + mapping: + Account: '#/components/schemas/AccountCategorization' + Exclusion: '#/components/schemas/ExclusionCategorization' + Split_Categorization: '#/components/schemas/SplitCategorization' + AccountNested: '#/components/schemas/NestedAccountCategorization' + OptionalAccountNested: '#/components/schemas/NestedOptionalCatetoryCategorization' + ExclusionNested: '#/components/schemas/NestedExclusionCategorization' + AccountCategorization: + type: object + properties: + id: + type: string + description: Derived ID from Account ID. + stable_name: + type: string + nullable: true + category: + type: string + display_name: + type: string + required: + - id + - category + - display_name + ExclusionCategorization: + type: object + properties: + id: + type: string + category: + type: string + display_name: + type: string + required: + - id + - category + - display_name + SplitCategorizationEntry: + type: object + properties: + amount: + type: integer + format: int64 + category: + $ref: '#/components/schemas/AccountCategorization' + SplitCategorization: + type: object + properties: + id: + type: string + default: "SPLIT" + category: + type: string + default: "SPLIT" + display_name: + type: string + default: "Split" + entries: + type: array + items: + $ref: '#/components/schemas/SplitCategorizationEntry' + required: + - entries + NestedApiCategorization: + type: object + oneOf: + - $ref: '#/components/schemas/NestedAccountCategorization' + - $ref: '#/components/schemas/NestedOptionalCatetoryCategorization' + - $ref: '#/components/schemas/NestedExclusionCategorization' + + NestedApiSubCategories: + type: array + items: + $ref: '#/components/schemas/NestedApiCategorization' + nullable: true + + NestedAccountCategorization: + type: object + properties: + id: + type: string + description: Derived ID from Account ID. + stable_name: + type: string + nullable: true + category: + type: string + display_name: + type: string + subCategories: + $ref: '#/components/schemas/NestedApiSubCategories' + + NestedOptionalCatetoryCategorization: + type: object + properties: + id: + type: string + description: Derived ID from Stable Name. + stable_name: + type: string + category: + type: string + display_name: + type: string + subCategories: + $ref: '#/components/schemas/NestedApiSubCategories' + + NestedExclusionCategorization: + type: object + properties: + id: + type: string + category: + type: string + display_name: + type: string + subCategories: + $ref: '#/components/schemas/NestedApiSubCategories' + + AccountName: + type: string + + Account: + type: object + properties: + id: + $ref: '#/components/schemas/AccountId' + accountStableName: + oneOf: + - $ref: '#/components/schemas/AccountStableName' + number: + type: integer + name: + $ref: '#/components/schemas/AccountName' + subAccounts: + type: array + items: + $ref: '#/components/schemas/Account' + nullable: true + normality: + $ref: '#/components/schemas/BankTransactionDirection' + balance: + type: integer + format: int64 + selfOnlyBalance: + type: integer + format: int64 + entries: + type: array + items: + $ref: '#/components/schemas/ApiLineItem' + ConfirmMatch: + type: object + properties: + match_id: + type: string + format: uuid + TaxAccountIdentifier: + type: object + oneOf: + - $ref: '#/components/schemas/LedgerAccountIdentifier' + - $ref: '#/components/schemas/TaxName' + discriminator: + propertyName: type + mapping: + Legder_Account_Id: '#/components/schemas/LedgerAccountIdentifier' + Tax_Name: '#/components/schemas/TaxName' + LedgerAccountIdentifier: + type: object + properties: + id: + type: string + required: + - id + TaxName: + type: object + properties: + name: + type: string + required: + - name + APIUnitAccount: + type: object + properties: + id: + type: string + account_name: + type: string + nullable: true + imported_at: + type: string + format: date-time + NestedApiLedgerAccount: + type: object + properties: + id: + $ref: '#/components/schemas/AccountId' + name: + type: string + stable_name: + oneOf: + - $ref: '#/components/schemas/AccountStableName' + normality: + $ref: '#/components/schemas/BankTransactionDirection' + account_type: + oneOf: + - $ref: '#/components/schemas/ApiLedgerAccountType' + account_subtype: + oneOf: + - $ref: '#/components/schemas/ApiLedgerAccountSubtype' + balance: + type: integer + format: int64 + entries: + type: array + items: + $ref: '#/components/schemas/ApiLineItem' + sub_accounts: + type: array + items: + $ref: '#/components/schemas/NestedApiLedgerAccount' + ApiBalanceTimestamp: + type: object + properties: + external_account_external_id: + type: string + external_account_source: + $ref: '#/components/schemas/TransactionSource' + balance: + type: integer + format: int64 + at: + type: string + format: date-time + created_at: + type: string + format: date-time + LedgerEntrySource: + type: object + oneOf: + - $ref: '#/components/schemas/TransactionLedgerEntrySource' + - $ref: '#/components/schemas/InvoiceLedgerEntrySource' + - $ref: '#/components/schemas/ManualLedgerEntrySource' + - $ref: '#/components/schemas/InvoicePaymentLedgerEntrySource' + - $ref: '#/components/schemas/RefundPaymentLedgerEntrySource' + - $ref: '#/components/schemas/OpeningBalanceLedgerEntrySource' + - $ref: '#/components/schemas/PayoutLedgerEntrySource' + discriminator: + propertyName: type + mapping: + Transaction_Ledger_Entry_Source: '#/components/schemas/TransactionLedgerEntrySource' + Invoice_Ledger_Entry_Source: '#/components/schemas/InvoiceLedgerEntrySource' + Manual_Ledger_Entry_Source: '#/components/schemas/ManualLedgerEntrySource' + Invoice_Payment_Ledger_Entry_Source: '#/components/schemas/InvoicePaymentLedgerEntrySource' + Refund_Ledger_Entry_Source: '#/components/schemas/RefundPaymentLedgerEntrySource' + Opening_Balance_Ledger_Entry_Source: '#/components/schemas/OpeningBalanceLedgerEntrySource' + Payout_Ledger_Entry_Source: '#/components/schemas/PayoutLedgerEntrySource' + TransactionLedgerEntrySource: + type: object + properties: + transaction_id: + type: string + format: uuid + external_id: + type: string + account_name: + type: string + date: + type: string + format: date-time + amount: + type: integer + format: int64 + direction: + $ref: '#/components/schemas/BankTransactionDirection' + counterparty: + type: string + nullable: true + entity_name: + type: string + default: "Bank Transaction" + display_description: + type: string + default: "Generated display description based on account name and date" + InvoiceLedgerEntrySource: + type: object + properties: + invoice_id: + type: string + format: uuid + external_id: + type: string + nullable: true + invoice_number: + type: string + nullable: true + recipient_name: + type: string + date: + type: string + format: date-time + amount: + type: integer + format: int64 + entity_name: + type: string + default: "Invoice" + display_description: + type: string + default: "Generated display description based on invoice number and date" + + ManualLedgerEntrySource: + type: object + properties: + manual_entry_id: + type: string + format: uuid + memo: + type: string + created_by: + type: string + entity_name: + type: string + default: "Manual Entry" + display_description: + type: string + default: "Manual Entry" + + InvoicePaymentLedgerEntrySource: + type: object + properties: + external_id: + type: string + nullable: true + invoice_id: + type: string + format: uuid + invoice_number: + type: string + nullable: true + amount: + type: integer + format: int64 + entity_name: + type: string + default: "Invoice Payment" + display_description: + type: string + default: "Payment on invoice based on invoice number" + + RefundPaymentLedgerEntrySource: + type: object + properties: + external_id: + type: string + nullable: true + refund_id: + type: string + format: uuid + refunded_to_customer_amount: + type: integer + format: int64 + recipient_name: + type: string + entity_name: + type: string + default: "Refund" + display_description: + type: string + default: "Refund of amount based on customer details" + + OpeningBalanceLedgerEntrySource: + type: object + properties: + account_name: + type: string + entity_name: + type: string + default: "Opening Balance Entry" + display_description: + type: string + default: "Opening balance for specified account" + + PayoutLedgerEntrySource: + type: object + properties: + payout_id: + type: string + format: uuid + external_id: + type: string + nullable: true + paid_out_amount: + type: integer + format: int64 + processor: + type: string + completed_at: + type: string + format: date-time + entity_name: + type: string + default: "Payout" + display_description: + type: string + default: "Payout processed by specified processor on specified date" + RefundPaymentMethod: + type: string + enum: + - CASH + - CHECK + - CREDIT_CARD + - ACH + - STORE_CREDIT + - OTHER + BankTransactionCategorization: + oneOf: + - $ref: '#/components/schemas/Category' + - $ref: '#/components/schemas/ApiSplitInput' + Category: + type: object + properties: + category: + $ref: '#/components/schemas/BankTransactionClassification' + SplitEntry: + type: object + properties: + amount: + type: integer + format: int64 + category: + $ref: '#/components/schemas/BankTransactionClassification' + BankTransactionClassification: + oneOf: + - $ref: '#/components/schemas/ExclusionClassification' + - $ref: '#/components/schemas/AccountIdentifier' + ApiSplitInput: + type: object + properties: + entries: + type: array + items: + $ref: '#/components/schemas/SplitEntry' + AuthToken: + type: object + properties: + access_token: + type: string + expires_in: + type: integer + format: int64 + token_type: + type: string + LineItem: + type: object + properties: + name: + type: string + display_name: + type: string + value: + type: integer + format: int64 + line_items: + type: array + items: + $ref: '#/components/schemas/LineItem' + is_contra: + type: boolean + required: + - name + - display_name + - value + - is_contra + BankTransactionDirection: + type: string + enum: + - DEBIT + - CREDIT + ApiTypedActivity: + type: object + required: + - createdAt + properties: + createdAt: + type: string + format: date-time + discriminator: + propertyName: type + mapping: + Categorize_Transaction_Activity: '#/components/schemas/ApiCategorizeBankTransactionActivity' + Load_PnL_Activity: '#/components/schemas/ApiLoadPnlActivity' + oneOf: + - $ref: '#/components/schemas/ApiCategorizeBankTransactionActivity' + - $ref: '#/components/schemas/ApiLoadPnlActivity' + + ApiCategorizeBankTransactionActivity: + type: object + allOf: + - $ref: '#/components/schemas/ApiTypedActivity' + properties: + transactionId: + type: string + format: uuid + categorizationMethod: + $ref: '#/components/schemas/ClassifierAgent' + categorization: + $ref: '#/components/schemas/BankTransactionCategorization' + required: + - transactionId + - categorizationMethod + - categorization + + ApiLoadPnlActivity: + type: object + allOf: + - $ref: '#/components/schemas/ApiTypedActivity' + properties: + pnlStartDate: + type: string + format: date-time + nullable: true + pnlEndDate: + type: string + format: date-time + nullable: true + required: + - createdAt + FlatUnitTransactionInput: + type: object + properties: + unit_transaction_id: + type: string + created_at: + type: string + format: date-time + transaction_type: + type: string + unit_account_id: + type: string + direction: + $ref: '#/components/schemas/BankTransactionDirection' + balance: + type: integer + format: int64 + tags: + type: string + nullable: true + amount: + type: integer + format: int64 + merchant_type: + type: string + nullable: true + merchant_location: + type: string + nullable: true + processed_counterparty_name: + type: string + nullable: true + card_last_4_digits: + type: string + nullable: true + description: + type: string + nullable: true + sec_code: + type: string + nullable: true + auto_categorize: + type: boolean + default: true + opening_balance_do_not_init: + type: boolean + opening_balance_init_to_zero: + type: boolean + init_external_transaction_mappings: + type: boolean + + FlatUnitTransactionResult: + type: object + properties: + id: + type: string + format: uuid + externalTransactionMappingId: + type: string + format: uuid + nullable: true + unit_transaction_id: + type: string + created_at: + type: string + format: date-time + transaction_type: + type: string + unit_account_id: + type: string + business_id: + type: string + format: uuid + direction: + $ref: '#/components/schemas/BankTransactionDirection' + balance: + type: integer + format: int64 + amount: + type: integer + format: int64 + merchant_type: + type: string + nullable: true + tags: + type: string + nullable: true + merchant_location: + type: string + nullable: true + processed_counterparty_name: + type: string + nullable: true + card_last_4_digits: + type: string + nullable: true + description: + type: string + nullable: true + sec_code: + type: string + nullable: true + category: + oneOf: + - $ref: '#/components/schemas/ApiCategorization' + nullable: true + categorizationStatus: + oneOf: + - $ref: '#/components/schemas/CategorizationStatus' + nullable: true + previouslyImported: + type: boolean diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/README.md b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/README.md new file mode 100644 index 00000000000..c89c478d15b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/README.md @@ -0,0 +1,32 @@ +# Mintlify Starter Kit + +Click on `Use this template` to copy the Mintlify starter kit. The starter kit contains examples including + +- Guide pages +- Navigation +- Customizations +- API Reference pages +- Use of popular components + +### Development + +Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) to preview the documentation changes locally. To install, use the following command + +``` +npm i -g mintlify +``` + +Run the following command at the root of your documentation (where mint.json is) + +``` +mintlify dev +``` + +### Publishing Changes + +Install our Github App to autopropagate changes from youre repo to your deployment. Changes will be deployed to production automatically after pushing to the default branch. Find the link to install on your dashboard. + +#### Troubleshooting + +- Mintlify dev isn't running - Run `mintlify install` it'll re-install dependencies. +- Page loads as a 404 - Make sure you are running in a folder with `mint.json` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/_snippets/snippet-example.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/_snippets/snippet-example.mdx new file mode 100644 index 00000000000..089334c54f9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/_snippets/snippet-example.mdx @@ -0,0 +1,3 @@ +## My Snippet + +This is an example of a reusable snippet diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/idempotency.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/idempotency.mdx new file mode 100644 index 00000000000..63657b32da1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/idempotency.mdx @@ -0,0 +1,5 @@ +--- +title: 'Idempotency' +--- + +Creation operations to the Layer API are idempotent when an external id is specified, such as the `external_id` field on a [Business](/api-reference/business/business) object. Others are indicated in the documentation. While these keys are not always required, use of idempotency keys is highly recommended whenever available. \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/json-data.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/json-data.mdx new file mode 100644 index 00000000000..65fa87f145b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/json-data.mdx @@ -0,0 +1,72 @@ +--- +title: 'JSON Data' +--- + +Layer uses a standardized json structure across all endpoints. There are only 3 possible top level fields. + +### Data + Contains the requested data from the endpoint and can be either a JSON list or a JSON object. + - Every top level object or item in an array will contain a `type` field indicating the entity type. + - `data` will only be present if the result is successful, in which case there will be a `2**` status code and no `error` field. + +```json +{ + "data":[ + { + "type":"Business", + "id":"08cee9cc-389e-44ea-a42e-ffb12670f515", + "external_id":"id-1", + "legal_name":"Acme, Inc.", + "tin":null, + "us_state":"AZ", + "entity_type":"LLC", + "phone_number":"53924476123", + "imported_at":"2023-07-13T17:25:59.292451Z", + "updated_at":"2023-07-13T17:25:59.292451Z", + "archived_at":null + }, + { + "type":"Business", + "id":"e348d217-1788-494c-9c1c-d8be13b89aba", + "external_id":"id-2", + "legal_name":"Acme, Inc.", + "tin":null, + "us_state":"AZ", + "entity_type":"LLC", + "phone_number":"69565771257", + "imported_at":"2023-07-13T17:25:59.720376Z", + "updated_at":"2023-07-13T17:25:59.720376Z", + "archived_at":null + } + ], + "meta":{ + + } +} +``` + +### Errors + An array of `error` objects. `error` objects will contain the following fields. + - `type`: One of a fixed set of categories. Helpful for categorizing & processing errors + - `description`: A human readable error description. + - `meta`: Optional additional information. +`errors` will be present only if the request is unsuccessful, in which case there will be a `4**` or `5**` status code and no `data` field. + +```json +{ + "errors": [ + { + "type": "Plaid", + "description": "Plaid credentials must be set before you can add plaid items to businesses. See /v1/configure/plaid" + } + ] +} + +``` + +### Meta +An optional object that may be used to communicate metadata about the request. +Example use cases include: +- Idempotency +- Pagination +- Rate limits \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/pagination.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/pagination.mdx new file mode 100644 index 00000000000..56737f879a7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/pagination.mdx @@ -0,0 +1,34 @@ +--- +title: 'Pagination' +--- + +Listing endpoints, for example [List all Businesses](/api-reference/business/list) and [List all Bank Transactions](/api-reference/bank-transactions/list) support cursor-based pagination. Pagination can be controlled via query string parameters: +- `sort_by` which supports some timestamp, integer and string keys. Valid sort keys are noted in documentation where applicable. Sort key is optional. +- `sort_order` specifies either ASC or DESC ordering for the sort key. Optional, ASC by default. +- `cursor` returned by the previous list request. Do not specify for the initial listing API call. +- `limit` constrains the number of results to be returned. Defaults to 100 for most endpoints. + +```bash Request +curl https://sandbox.layerfi.com/v1/businesses/:business_id/bank-transactions?sort_by=date&sort_order=DESC&limit=50 \ + -H "Authorization: Bearer " +``` + +Responses to paginating endpoints will include pagination data in the meta response field. + +```json Response +{ + "data": [ + // Data omitted + ], + "meta": { + "pagination": { +"sort_by": "date", + "sort_order": ASC, + "cursor": "VGhhbmtzIGZvciByZWFsbHkgcmVhZGluZyB0aGUgZG9jdW1lbnRhdGlvbiE=" + "has_more": true + } + } +} +``` + +When the `has_more` field is true, use the returned cursor in an additional request to fetch the next page of results. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/rate-limiting.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/rate-limiting.mdx new file mode 100644 index 00000000000..cb521ffbcba --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-details/rate-limiting.mdx @@ -0,0 +1,16 @@ +--- +title: 'Rate Limiting' +--- + +Layer uses a single API-wide endpoint cost rate limiting system, where every endpoint contributes a cost towards a total budget. The budget follows a bucket model, where each API call uses tokens from a current bucket which is refilled every 1 second. + +Rate limits should never be approached for normal customer read operations, and are only intended to limit heavy data ingestion processes, such as en-masse backfills of data. Nonetheless, we recommend adding retries on all Layer API calls. To assist in retries, the following headers are included in every successful API request: +- `X-RateLimit-Limit`: a specified bucket capacity. +- `X-RateLimit-Remaining`: the number of tokens remaining in a bucket. +- `X-RateLimit-Reset`: a UTC timestamp (in seconds) that specifies the time of refilling a bucket. + +Any API calls that are rate limited will receive a response with: +- Error code `429: Too Many Requests` +- `Retry-After` header indicating (in seconds) how long to wait before retrying the request. + +During normal operations, adding enough retries that requests will retry 1 second later should be sufficient to avoid any rate limiting. \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/add-custom-account-for-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/add-custom-account-for-business.mdx new file mode 100644 index 00000000000..2eec66291f4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/add-custom-account-for-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/custom-accounts/ +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-invoice-payment-allocations.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-invoice-payment-allocations.mdx new file mode 100644 index 00000000000..761bb7ee097 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-invoice-payment-allocations.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/invoices/payments/allocations/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-invoices.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-invoices.mdx new file mode 100644 index 00000000000..a3c45d1a000 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-invoices.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/invoices/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-payouts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-payouts.mdx new file mode 100644 index 00000000000..f4076b0108d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-payouts.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/payouts/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-transactions.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-transactions.mdx new file mode 100644 index 00000000000..ed4d0bf2fa4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/apply-list-of-tags-to-list-of-transactions.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/bank-transactions/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-business.mdx new file mode 100644 index 00000000000..be8a7b31e86 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId}/archive +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-custom-account-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-custom-account-from-business.mdx new file mode 100644 index 00000000000..ecb22389c48 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-custom-account-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/custom-accounts/{customAccountId}/archive +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-linked-external-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-linked-external-account.mdx new file mode 100644 index 00000000000..a01c50eab7b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-linked-external-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/external-accounts/{accountId}/archive +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-plaid-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-plaid-account.mdx new file mode 100644 index 00000000000..445181466b5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/archive-plaid-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/plaid/accounts/{plaidAccountId}/archive +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/bank-transaction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/bank-transaction.mdx new file mode 100644 index 00000000000..9f93e7df667 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/bank-transaction.mdx @@ -0,0 +1,135 @@ +--- +title: "Bank Transaction object" +--- +Bank transactions are transactions that have occurred within a bank account owned by a business. + +### Attributes + + + Unique identifier for the bank transaction. + + + Unique ID of the bank transaction in your system for linking purposes. **Idempotency key.** + + + Resource type. Value will be "Bank_Transaction". + + + Id for the Business this transaction belongs to. + + + The source that the bank transaction was imported from. + Values can be: `UNIT`, `PLAID`, `API` + + + Unique ID of the bank transaction in its source system. **Idempotency key.** + + + Id of the source account in the source system. + + + Date the transaction occurred. + + + The direction of the transaction relative to the source account. + Values can be: `CREDIT`, `DEBIT` + + + The amount of the transaction in cents. + + + The name of the merchant or counterparty associated with the transaction. + + + Description of the transaction. + + + The type of bank account transaction. + Example values: `PURCHASE`, `BOOK`, `ATM`, `WIRE`, etc. + + + The status of the transaction's categorization in Layer's systems. + Values can be: `PENDING`, `READY_FOR_INPUT`, `CATEGORIZED`, `LAYER_REVIEW` + + + How the transaction was categorized. + Values can be: `SMS`, `API`, `LAYER_AUTO`, `LAYER_MANUAL` + + + The category assigned to the transaction. Only populated for transactions that have a finalized category. + + + String enum for the category assigned to the transaction. The set of category enums will vary based on chart of account configured for the business. + + + A human-readable string describing the category. This can be presented to the end user in your UI. + + + + + Layer's suggested categorization for the transaction. + + + The type of categorization approach used. + + + The category assigned to the transaction. Only populated for transactions that have a finalized category. + + + String enum for the category assigned to the transaction. The set of category enums will vary based on chart of account configured for the business. + + + A human-readable string describing the category. This can be presented to the end user in your UI. + + + + + Layer's list of suggested categories for the transaction. + + + String enum for the category assigned to the transaction. The set of category enums will vary based on chart of account configured for the business. + + + A human-readable string describing the category. This can be presented to the end user in your UI. + + + + + + + + + + +```json Example +{ + "id":"67cee0d8-3b8e-4b4b-a857-78ce3bb1d895", + "type":"Bank_Transaction", + "transaction_type":"Purchase", + "business_id":"cfee5365-dcc3-425e-b403-cc9568f7121e", + "source":"API", + "source_transaction_id":"11111113", + "source_account_id":"111113", + "imported_at":"2023-06-07T00:42:08.664543Z", + "date":"2023-05-15T14:13:07Z", + "direction":"Debit", + "amount":8026, + "counterparty_name":"SUNOCO", + "description":null, + "categorization_status":"CATEGORIZED", + "category":{ + "category":"FUEL", + "display_name":"Fuel" + }, + "categorization_method":"LAYER_AUTO", + "categorization_flow":{ + "type":"AUTO", + "category":{ + "category":"FUEL", + "display_name":"Fuel" + } + } +} +``` + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/bulk-match-transactions.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/bulk-match-transactions.mdx new file mode 100644 index 00000000000..33936198586 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/bulk-match-transactions.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/bank-transactions/bulk-match +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/business.mdx new file mode 100644 index 00000000000..cff51d74cf9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/business.mdx @@ -0,0 +1,85 @@ +--- +title: "Business object" +--- +A Business is Layer’s representation of an end business on your platform. The Business object contains all the information about the business needed for Layer’s embedded accounting logic. + +### Attributes + + + Unique identifier for the business. + + + Resource type. Value will be "Business". + + + Unique ID of the business in your system for linking purposes. **Idempotency key.** + + + Legal name of the business as it has been registered. + + + Tax identification number of the business. + + + Two letter state abbreviation. (`AK`, `AL`, `AR`, etc.) + + + Entity type of the business. Used to determine tax filing status. + Values can be: `SOLE_PROP`, `C_CORP`, `LLC`, `S_CORP`, `PARTNERSHIP` + + + Phone number used for SMS based categorization. + + + List of unit accounts associated with this business. + + + The Unit account's ID + + + + + Plaid items linked to this account. + + + `item_id` returned by Plaid on the initial link. + + + `access_token` returned by Plaid on the initial link. + + + + + Time when the business entity was created in Layer. **Eligible sort key.** + + + Time when the business' information was last updated in Layer. **Eligible sort key.** + + + + + +```json Example +{ + "id": "863ed926-e30d-40f4-8e7e-b0d5387ce4fb", + "type": "Business", + "external_id": "test-acme-id", + "legal_name": "ACME LLC", + "tin": null, + "business_activity_code": null, + "us_state": "CA", + "entityType": "LLC", + "phone_number": "+16504651359", + "imported_at": "2023-06-15T22:12:05.467940Z", + "updated_at": "2023-06-15T22:12:05.467940Z", + "archived_at": null, + "unit_accounts": [ + { + "id": "111111", + "imported_at": "2023-06-15T22:12:05.467940Z" + } + ] +} +``` + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/calculate-new-custom-account-opening-balance-entry.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/calculate-new-custom-account-opening-balance-entry.mdx new file mode 100644 index 00000000000..78d1172290d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/calculate-new-custom-account-opening-balance-entry.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/custom-accounts/{customAccountId}/opening-balance +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/categorize-transaction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/categorize-transaction.mdx new file mode 100644 index 00000000000..1f8a738fce6 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/categorize-transaction.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId}/bank-transactions/{transactionId}/categorize +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/configure-plaid-client-id-and-secret.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/configure-plaid-client-id-and-secret.mdx new file mode 100644 index 00000000000..84fc4beb081 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/configure-plaid-client-id-and-secret.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/configure/plaid +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/configure-stripe-secret.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/configure-stripe-secret.mdx new file mode 100644 index 00000000000..5edde7523aa --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/configure-stripe-secret.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/configure/stripe +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-custom-account-balance-timestamp.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-custom-account-balance-timestamp.mdx new file mode 100644 index 00000000000..290c3b8c667 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-custom-account-balance-timestamp.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/custom-accounts/{customAccountId}/balance +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-invoice-payments-from-batch.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-invoice-payments-from-batch.mdx new file mode 100644 index 00000000000..6a6094d10d7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-invoice-payments-from-batch.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/invoices/payments/ +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-invoices-from-batch.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-invoices-from-batch.mdx new file mode 100644 index 00000000000..907bf624a3a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-invoices-from-batch.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/invoices/bulk +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-ledger-account-for-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-ledger-account-for-business.mdx new file mode 100644 index 00000000000..9bd9f5fdcee --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-ledger-account-for-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/ledger/accounts +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-manual-ledger-entries.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-manual-ledger-entries.mdx new file mode 100644 index 00000000000..a6ae9c63c05 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-manual-ledger-entries.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/ledger/manual-entries +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-new-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-new-business.mdx new file mode 100644 index 00000000000..15d16b405ad --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-new-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-new-payout-for-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-new-payout-for-business.mdx new file mode 100644 index 00000000000..cc98feba1dc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-new-payout-for-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/payouts/ +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-plaid-item-link-token.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-plaid-item-link-token.mdx new file mode 100644 index 00000000000..b6bfbd4cfd7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-plaid-item-link-token.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/plaid/link +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-reversal-entry-for-ledger-entry.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-reversal-entry-for-ledger-entry.mdx new file mode 100644 index 00000000000..bbecbb70c84 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/create-reversal-entry-for-ledger-entry.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/ledger/entries/{entryId}/reverse +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-invoice-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-invoice-from-business.mdx new file mode 100644 index 00000000000..77b02a9b6c2 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-invoice-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/invoices/{invoiceId}/delete +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-invoice-payment-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-invoice-payment-from-business.mdx new file mode 100644 index 00000000000..138c1245756 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-invoice-payment-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/invoices/payments/{paymentId}/delete +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-list-of-payout-tags.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-list-of-payout-tags.mdx new file mode 100644 index 00000000000..9cebc9d5df9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-list-of-payout-tags.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /v1/businesses/{businessId}/payouts/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-plaid-item-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-plaid-item-from-business.mdx new file mode 100644 index 00000000000..b035f93a1cc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-plaid-item-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /v1/businesses/{businessId}/plaid/items/{plaidItemPlaidId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-tags-applied-to-payment-allocation.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-tags-applied-to-payment-allocation.mdx new file mode 100644 index 00000000000..c897bc6ead1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/delete-tags-applied-to-payment-allocation.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /v1/businesses/{businessId}/invoices/payments/{paymentId}/allocations/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/download-ledger-transactions-from-business-as-csv.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/download-ledger-transactions-from-business-as-csv.mdx new file mode 100644 index 00000000000..c49c0654927 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/download-ledger-transactions-from-business-as-csv.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/reports/download-transactions-csv +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/exchange-plaid-token-and-add-item-to-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/exchange-plaid-token-and-add-item-to-business.mdx new file mode 100644 index 00000000000..48d74683922 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/exchange-plaid-token-and-add-item-to-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/plaid/link/exchange +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-business-auth-token.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-business-auth-token.mdx new file mode 100644 index 00000000000..163a391e635 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-business-auth-token.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{id}/auth-token +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-business.mdx new file mode 100644 index 00000000000..a267166494f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-chart-of-accounts-for-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-chart-of-accounts-for-business.mdx new file mode 100644 index 00000000000..e5645f65895 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-chart-of-accounts-for-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/balances +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-chart-of-accounts-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-chart-of-accounts-from-business.mdx new file mode 100644 index 00000000000..80c63d7d557 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-chart-of-accounts-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/ledger/chart +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-client-info.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-client-info.mdx new file mode 100644 index 00000000000..947dc60485b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-client-info.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /whoami +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-custom-account-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-custom-account-from-business.mdx new file mode 100644 index 00000000000..82bad9fade5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-custom-account-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/custom-accounts/{customAccountId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-invoice-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-invoice-from-business.mdx new file mode 100644 index 00000000000..a2b150869b8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-invoice-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/invoices/{invoiceId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-invoice-payment-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-invoice-payment-from-business.mdx new file mode 100644 index 00000000000..cdae1d6434a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-invoice-payment-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/invoices/payments/{paymentId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-ledger-entry-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-ledger-entry-from-business.mdx new file mode 100644 index 00000000000..7d38d6388d5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-ledger-entry-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/ledger/entries/{entryId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-ledger-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-ledger-from-business.mdx new file mode 100644 index 00000000000..78123de9a11 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-ledger-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/ledger/accounts +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-linked-external-account-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-linked-external-account-from-business.mdx new file mode 100644 index 00000000000..92ef2280baf --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-linked-external-account-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/external-accounts/{accountId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-opening-balance-from-linked-external-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-opening-balance-from-linked-external-account.mdx new file mode 100644 index 00000000000..0ce3db0bcc7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-opening-balance-from-linked-external-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/external-accounts/{accountId}/opening-balance +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-or-create-ledger-account-on-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-or-create-ledger-account-on-business.mdx new file mode 100644 index 00000000000..951cfec4672 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-or-create-ledger-account-on-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/ledger/accounts/{accountId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-payout-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-payout-from-business.mdx new file mode 100644 index 00000000000..88be7e05b1b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-payout-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/payouts/{payoutId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-account-configuration.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-account-configuration.mdx new file mode 100644 index 00000000000..d64bafb6827 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-account-configuration.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/plaid/accounts/{plaidAccountId}/configuration +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-client-id-and-secret.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-client-id-and-secret.mdx new file mode 100644 index 00000000000..0b317f73077 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-client-id-and-secret.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/configure/plaid +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-item-configuration.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-item-configuration.mdx new file mode 100644 index 00000000000..2e41a0416d5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-plaid-item-configuration.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/plaid/items/{plaidItemPlaidId}/configuration +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-profit-and-loss-entries-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-profit-and-loss-entries-from-business.mdx new file mode 100644 index 00000000000..c3ebc7989b7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-profit-and-loss-entries-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/reports/profit-and-loss-entries +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-profit-and-loss-report-for-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-profit-and-loss-report-for-business.mdx new file mode 100644 index 00000000000..c713f68cc6e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-profit-and-loss-report-for-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/reports/profit-and-loss +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-stripe-secret.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-stripe-secret.mdx new file mode 100644 index 00000000000..b30774c7bf8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-stripe-secret.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/configure/stripe +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-transaction-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-transaction-from-business.mdx new file mode 100644 index 00000000000..e6e7439d413 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-transaction-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/bank-transactions/{transactionId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-transactions-from-linked-external-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-transactions-from-linked-external-account.mdx new file mode 100644 index 00000000000..720b874ec29 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-transactions-from-linked-external-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/external-accounts/{accountId}/transactions +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-v1businesses-plaidaccounts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-v1businesses-plaidaccounts.mdx new file mode 100644 index 00000000000..38516d93eda --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/get-v1businesses-plaidaccounts.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/plaid/accounts +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/import-transactions.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/import-transactions.mdx new file mode 100644 index 00000000000..d7f210af607 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/import-transactions.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/unit-transactions +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/invoice.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/invoice.mdx new file mode 100644 index 00000000000..d3d2fd876f1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/invoice.mdx @@ -0,0 +1,261 @@ +--- +title: "Invoice object" +--- + +An Invoice represents an invoice that a Business has collected for goods and services provided. Invoices are used to pass information about sales and accounts receivable into Layer. + +### Attributes + + + Unique identifier for the invoice. + + + Resource type. Value will be "Invoice". + + + Unique ID of the invoice in your system for linking purposes. + + + Id of the Business that generated the invoice. + + + Status of the invoice. Values can be: `PENDING`, `SENT`, `PARTIALLY_PAID`, + `PAID`, `VOIDED` + + + When the invoice was sent by the business to the recipient. + + + When the invoice is due. + + + When the invoice was paid. + + + When the invoice was voided. Voiding excludes the invoice from accounting. + + + Number for the invoice for display to end-users. + + + String description of the invoice recipient. + + + Line items making up the invoice. + + + Id of the invoice line item. + + + Id of the parent invoice + + + Description of the specific line item. + + + Reference to the product being sold. + + + Number of units sold. + + + The amount in cents of each unit. + + + Total discount given to this line item, in cents. + + + + + + Ledger account associated with this tax. + + Type of tax account object. + + + Name of the tax account, if a name was specified when this line item was created. + + + Id of the tax account if either an account ID was used to create the tax line item or if no tax account was specified. + + + + + Amount, in cents, of tax owed. + + + + + Total discount given to this line item, in cents. + + + Indicates this line item is a prepayment for future services, e.g. package deals, gift cards, etc. + + + + + Subtotal of all invoice line items in cents. + + + Additional discount applied to the whole invoice in addition to individual + line items. + + + Sum of all discount amounts across the invoice line items and any additional + discounts in cents. + + + + + + Ledger account associated with this tax. + + Type of tax account object. + + + Name of the tax account, if a name was specified when this line item was created. + + + Id of the tax account if either an account ID was used to create the tax line item or if no tax account was specified. + + + + + Amount, in cents, of tax owed. + + + + + Sum of all taxes across the invoice line items and any additional taxes in + cents. + + + Tips included by the buyer, in cents. + + + Total amount of the invoice in cents. + + + The remaining balance on the invoice after factoring in all previous invoice + payments. + + + The payments that have been made towards the balance of the invoice. + + + Id of the invoice payment. + + + External id for the payment within your platform. **Idempotency key.** + + + Method used to make the payment. Values can be: `CASH`, `CHECK`, + `CREDIT_CARD`, `ACH`, `REDEEMED_PREPAYMENT`, `OTHER` + + + Fee associated with processing a payment, e.g. credit card processing + fees, in cents. + + + When the buyer payment occurred. + + + When the invoice was imported into Layer. + + + Processor used to make the payment, if any. + Any processor name can be provided and will be tracked. + Supported processors (`STRIPE`, `SHOPIFY`) will have additional asset balance tracking. + + + + + Id of the invoice being paid. + + + Id of the invoice payment. + + + Amount paid towards this invoice in cents. Cannot exceed the amount of + the associated payment. + + + + + + + Time when the invoice was first imported into Layer. **Eligible sort key.** + + + Time when the invoice was last updated in Layer. **Eligible sort key.** + + + +```json Response +{ + "data": { + "type": "Invoice", + "id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "business_id": "83d8fb80-31ee-4d57-b684-44b4aaa5e01f", + "external_id": "019234", + "status": "SENT", + "sent_at": "2024-04-02T09:02:00Z", + "due_at": "2023-04-02T09:02:00Z", + "paid_at": null, + "voided_at": null, + "invoice_number": "1", + "recipient_name": "John Doe", + "line_items": [ + { + "id": "e6a491dd-9c22-4403-a54f-32d741a7ec67", + "invoice_id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "account_identifier": null, + "description": null, + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": "2.00", + "subtotal": 2598, + "discount_amount": 0, + "sales_taxes_total": 218, + "sales_taxes": [ + { + "tax_account": { + "type": "Tax_Name", + "name": "CALIFORNIA_VAT" + }, + "amount": 218 + } + ], + "total_amount": 2816 + }, + { + "id": "44f06385-3ef5-4517-8095-eeedaf2054ab", + "invoice_id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "account_identifier": null, + "description": null, + "product": "Full drain cleaning service", + "unit_price": 25000, + "quantity": "1.00", + "subtotal": 25000, + "discount_amount": 0, + "sales_taxes_total": 0, + "total_amount": 25000 + } + ], + "subtotal": 27598, + "additional_discount": 250, + "additional_sales_taxes_total": 0, + "tips": 0, + "total_amount": 27566, + "outstanding_balance": 27566, + "payment_allocations": [], + "imported_at": "2024-04-19T02:23:59.902537Z", + "updated_at": null, + "transaction_tags": [] + } +} +``` + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/ledger.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/ledger.mdx new file mode 100644 index 00000000000..8e2de6ee3ad --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/ledger.mdx @@ -0,0 +1,237 @@ +--- +title: "General Ledger objects" +--- +The general ledger of a business on Layer’s platform. A general ledger is automatically created when a [Business](/api-reference/business/business) is created via the [Create a Business](/api-reference/business/create) endpoint. + +### Attributes + + + Resource type. Value will be "Chart_Of_Accounts". + + + Array of [Ledger Accounts](/api-reference/ledger#ledger-account-object) within the Ledger. + + +#### Ledger Account object +An account within the general ledger + + Unique identifier of the ledger account. + + + Account name + + + Stable identifier for templated account. + + + Human readable description for this ledger account. + + + An array of [Ledger Account](/api-reference/ledger#ledger-account-object) objects representing child accounts of this ledger account. + + + Balance of the account, in cents. + + + Account balance normality. + Values can be: `DEBIT`, `CREDIT` + + + Array of [Ledger Account Line Item](/api-reference/ledger#ledger-account-line-item-object) objects representing ledger entries recorded in this account. + + +#### Ledger Account Line Item object +A ledger entry within a ledger account. + + Unique identifier of the ledger account line item. + + + Id of the Journal entry the ledger account line item was part of. + + + Simplified [Ledger Account](/api-reference/ledger#ledger-account-object) object containing this line item. + + + Direction of line item. + Values can be: `DEBIT`, `CREDIT` + + + Timestamp of the financial transaction associted with the line item. + + + Timestamp when ledger entry was added to the ledger account. + + +#### Journal Entry object +A journal entry within the general ledger. + + Unique identifier of the journal entry. + + + Id of the business associated with this journal entry. + + + Id of the general ledger containing this journal entry. + + + Entity that created the journal entry. + + + Type of entry. + Example values: `INVOICE_PAYMENT`, `EXPENSE` + + + Array of [Ledger Account Line Items](/api-reference/ledger#ledger-account-line-item-object) comprising the Journal Entry. + + + + +```json Example +{ + "data":{ + "type":"Chart_Of_Accounts", + "name":"Default", + "accounts":[ + { + "id":"86b497b9-71e3-4353-9726-8b4a5ac46626", + "number":0, + "pnlCategory":null, + "name":"Assets", + "accountStableName":"ASSETS", + "description":"All asset accounts for your business", + "subAccounts":[ + { + "id":"b0b4f2ef-9c46-4ee4-87f6-3db37cad4d5d", + "number":0, + "pnlCategory":null, + "name":"JP Morgan Chase Business Checking (4402)", + "accountStableName":"UNIT_CHECKING", + "description":"Unit checking", + "normality":"DEBIT", + "balance":478018, + "selfOnlyBalance":478018, + "entries":[ + { + "id":"63c52976-16a4-486a-86ab-00d2cc669e99", + "entry_id":"0e81ec02-483a-4ade-8878-b8e731e14c0f", + "account":{ + "id":"b0b4f2ef-9c46-4ee4-87f6-3db37cad4d5d", + "name":"JP Morgan Chase Business Checking (4402)", + "stable_name":"UNIT_CHECKING", + "normality":"DEBIT", + "pnl_category":null, + "always_show_in_pnl":false, + "description":"Unit checking" + }, + "amount":8531, + "direction":"CREDIT", + "entry_at":"2023-12-11T16:11:41.316Z", + "createdAt":"2023-12-11T16:11:43.875713Z" + }, + { + "id":"91c7b1b6-5ab5-4b11-b39d-1adf33841f11", + "entry_id":"e9394916-91d2-4ddb-8bfe-bbc25bb7d9da", + "account":{ + "id":"b0b4f2ef-9c46-4ee4-87f6-3db37cad4d5d", + "name":"JP Morgan Chase Business Checking (4402)", + "stable_name":"UNIT_CHECKING", + "normality":"DEBIT", + "pnl_category":null, + "always_show_in_pnl":false, + "description":"Unit checking" + }, + "amount":540981, + "direction":"DEBIT", + "entry_at":"2023-12-11T16:11:41.316Z", + "createdAt":"2023-12-11T16:11:43.949003Z" + } + ] + }, + { + "id":"bd68a8e3-fce4-4e7f-bee3-e7bd0f94627d", + "number":0, + "pnlCategory":null, + "name":"Accounts Receivable", + "accountStableName":"ACCOUNTS_RECEIVABLE", + "description":"Amounts owed by clients", + "normality":"DEBIT", + "balance":140485, + "selfOnlyBalance":140485, + "entries":[ + // Omitteed for length + ] + } + // Omitted for length + ], + "normality":"DEBIT", + "balance":618503, + "entries":[ + + ] + }, + { + "id":"d138a41a-0ea4-4949-a3c2-28279936fda1", + "number":0, + "pnlCategory":null, + "name":"Liabilities", + "accountStableName":"LIABILITIES", + "description":"All liabilities for your business", + "subAccounts":[ + // Omitted for brefity + ], + "normality":"CREDIT", + "entries":[ + + ] + }, + { + "id":"436a0187-1355-4e2d-978a-5ff4d52ad03f", + "number":0, + "pnlCategory":null, + "name":"Equity", + "accountStableName":"EQUITY", + "description":"All equity accounts for your business", + "subAccounts":[ + // Omitted for brefity + ], + "normality":"CREDIT", + "entries":[ + ] + }, + { + "id":"337ae99d-68a9-4ec2-b19d-4b8a5750f6b9", + "number":0, + "pnlCategory":"INCOME", + "name":"Revenue", + "accountStableName":"REVENUE", + "subAccounts":[ + // Omitted for brefity + ], + "normality":"CREDIT", + "balance":678466, + "entries":[ + + ] + }, + { + "id":"4f394bcb-e514-4ec9-bcd7-6612ebd146fe", + "number":0, + "pnlCategory":null, + "name":"Expenses", + "accountStableName":"EXPENSES", + "description":"Expenses", + "subAccounts":[ + // Omitted for brefity + ], + "normality":"DEBIT", + "balance":65963, + "entries":[ + + ] + } + ] + } +} +``` + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-account-balances-from-ledger.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-account-balances-from-ledger.mdx new file mode 100644 index 00000000000..ce9abba3fcb --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-account-balances-from-ledger.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/ledger/balances +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-balance-timestamps-from-linked-external-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-balance-timestamps-from-linked-external-account.mdx new file mode 100644 index 00000000000..846c88ef833 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-balance-timestamps-from-linked-external-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/external-accounts/{accountId}/balance-timestamps +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-business-activities.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-business-activities.mdx new file mode 100644 index 00000000000..4fca4699b8a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-business-activities.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/activity/businesses/{businessId}/activities +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-businesses.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-businesses.mdx new file mode 100644 index 00000000000..749589fb72d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-businesses.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-chart-of-accounts-categories-for-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-chart-of-accounts-categories-for-business.mdx new file mode 100644 index 00000000000..94c889afb78 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-chart-of-accounts-categories-for-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/categories +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-custom-accounts-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-custom-accounts-from-business.mdx new file mode 100644 index 00000000000..00b2987380c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-custom-accounts-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/custom-accounts/ +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-invoice-payments-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-invoice-payments-from-business.mdx new file mode 100644 index 00000000000..d654fcd0da0 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-invoice-payments-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/invoices/payments +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-ledger-account-line-items.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-ledger-account-line-items.mdx new file mode 100644 index 00000000000..66f6eb23448 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-ledger-account-line-items.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/ledger/accounts/{accountId}/lines +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-ledger-entries-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-ledger-entries-from-business.mdx new file mode 100644 index 00000000000..b272a47875c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-ledger-entries-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/ledger/entries +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-linked-external-accounts-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-linked-external-accounts-from-business.mdx new file mode 100644 index 00000000000..e3bfe012b34 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-linked-external-accounts-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/external-accounts/ +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-payouts-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-payouts-from-business.mdx new file mode 100644 index 00000000000..7c4780bd35e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-payouts-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/payouts/ +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-plaid-accounts-for-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-plaid-accounts-for-business.mdx new file mode 100644 index 00000000000..445181466b5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-plaid-accounts-for-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/plaid/accounts/{plaidAccountId}/archive +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-plaid-items-for-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-plaid-items-for-business.mdx new file mode 100644 index 00000000000..8f928918d13 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-plaid-items-for-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/plaid/items +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-refunds-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-refunds-from-business.mdx new file mode 100644 index 00000000000..3021d81d1e5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-refunds-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/invoices/refunds/ +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice-payment-allocation.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice-payment-allocation.mdx new file mode 100644 index 00000000000..4a6c6055be7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice-payment-allocation.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/invoices/payments/{paymentId}/allocations/{allocationId}/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice-payment.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice-payment.mdx new file mode 100644 index 00000000000..bb09b7c8278 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice-payment.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/invoices/payments/{paymentId}/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice.mdx new file mode 100644 index 00000000000..63cae9d93ce --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-invoice.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/invoices/{invoiceId}/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-payout.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-payout.mdx new file mode 100644 index 00000000000..2a2119388fc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-payout.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/payouts/{payoutId}/tags/ +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-transaction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-transaction.mdx new file mode 100644 index 00000000000..0712577d3f9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-tags-applied-to-transaction.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/bank-transactions/{transactionId}/tags +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-transaction-ledger-entries.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-transaction-ledger-entries.mdx new file mode 100644 index 00000000000..4bee0c1f7e8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-transaction-ledger-entries.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/bank-transactions/{transactionId}/ledger-entries +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-transactions-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-transactions-from-business.mdx new file mode 100644 index 00000000000..e399217c455 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/list-transactions-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/businesses/{businessId}/bank-transactions +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/match-transaction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/match-transaction.mdx new file mode 100644 index 00000000000..1f6ba0d108f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/match-transaction.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId}/bank-transactions/{transactionId}/match +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/payments.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/payments.mdx new file mode 100644 index 00000000000..ba041cc2c00 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/payments.mdx @@ -0,0 +1,81 @@ +--- +title: "Invoice Payment object" +--- + +A refund represents a transaction that returns value to from a business to a customer. A specific payment can be refunded or a general refund can be applied to an invoice. + +### Attributes + + + Unique identifier for the payment. + + + Unique ID of the invoice payment in an external system for linking and + idempotency. + + + Timestamp when the payment was completed. + + + Payment method. Possible values are: `CASH`, `CHECK`, `CREDIT_CARD`, `ACH`, + `REDEEMED_PREPAYMENT`, `OTHER` + + + Fee paid by business for processing of payment in positive cents. + + + Customer payment amount, in cents. + + + Processor used to make the payment, if any. + Any processor name can be provided and will be tracked. + Supported processors (`STRIPE`, `SHOPIFY`) will have additional asset balance tracking. + + + Timestamp when the payment was imported into Layer. + + + Timestamp when the payment was imported into Layer. + + + Id of an invoice to which this payment is be applied. + + + Id of the payment this this allocationa applies from. + + + Customer payment amount, in cents. + + + + + +```json Response + { + "data": { + "type": "Payment", + "id": "e67c216b-28f4-4a0e-9a21-7f05c19e4c66", + "external_id": "payment-1", + "at": "2024-02-27T02:16:40.369432Z", + "method": "CREDIT_CARD", + "fee": 20, + "amount": 90, + "processor": "STRIPE", + "imported_at": "2024-02-27T02:16:40.389772Z", + "allocations": [ + { + "invoice_id": "57f0fada-bb56-4f3e-9afa-2a222b68009e", + "payment_id": "e67c216b-28f4-4a0e-9a21-7f05c19e4c66", + "amount": 90, + "transaction_tags": [] + } + ], + "transaction_tags": [] + } + } + ``` + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/plaid.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/plaid.mdx new file mode 100644 index 00000000000..5f4d7d9d25d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/plaid.mdx @@ -0,0 +1,31 @@ +--- +title: "Plaid Configuration object" +--- +Layer uses Plaid to connect to pull information from your customers' external bank accounts and credit cards. The Plaid Configuration object allows you to manage the Plaid configuration associated with your Layer account. + +### Attributes + + + Resource type. Value will be: "Plaid_Configuration" + + + Account-wide client id. + + + Account-wide plaid secret. Only the last 4 characters will be displayed. + + + + + +```json Example +{ + "data":{ + "type":"Plaid_Configuration", + "client_id":"6488fafd7a73ae00122004d6", + "secret_last_4":"acae" + } +} +``` + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/pnl.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/pnl.mdx new file mode 100644 index 00000000000..8ff60453804 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/pnl.mdx @@ -0,0 +1,171 @@ +--- +title: "Profit and Loss object" +--- +A Profit and Loss object represents a profit and loss report that has been generated for a [Business](/api-reference/business/business). + +Profit and Loss reports are generated for a specific time range and only contain information for categorized transactions that have been journaled to the general ledger. + +### Attributes + + + Id of the Business the profit and loss report was generated for. + + + Resource type. Value will be "Profit_And_Loss" + + + Start date for data included in the report. + + + End date for data in cluded in the report. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing the income line items in the report. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing the cost of goods sold line items in the report. + + + Gross profit in cents. Calculated by subtracting total cost_of_sales line items from total income line items. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing the expense line items in the report. + + + Net profit before taxes in cents. Calculated by subtracting total expenses line items from gross_profit. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing the tax line items in the report. + + + Net profit in cents. Calculated by subtracting total taxes line items from profit_before_taxes + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing business outflow transactions that are not part of the profit and loss statement. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing personal expense transactions that are not part of the profit and loss statement. + + + A boolean representing whether all imported transactions within the report date range have been categorized. If FALSE, there are uncategorized transactions within the selected date range, indicating the provided report data may be incomplete. + + +#### `Line Item` object + + Enum name for the line item. Ex. "REVENUE" + + + Display name for the line item. Ex. "Revenue" + + + The value of the line item in cents. + + + An array of [Line Item](/api-reference/pnl/pnl#lineitem-object) objects representing child line items within the report section. + + + +```json Profit and Loss +{ + "data":{ + "type":"Profit_And_Loss", + "business_id":"d2f6d97f-3345-4299-9ec2-468738c5d536", + "start_date":"2023-01-01T06:00:00Z", + "end_date":"2023-12-01T06:00:00Z", + "income":{ + "name":"REVENUE", + "display_name":"Revenue", + "value":49397, + "line_items":[ + { + "name":"SERVICES_REVENUE", + "display_name":"Service Revenue", + "value":46897, + "line_items":null + }, + { + "name":"GOODS_REVENUE", + "display_name":"Sale of Goods Revenue", + "value":0, + "line_items":null + }, + { + "name":"DISCOUNTS", + "display_name":"Discounts & Refunds", + "value":2500, + "line_items":null + } + ] + }, + "cost_of_goods_sold":{ + "name":"COGS", + "display_name":"Cost of Goods Sold", + "value":8026, + "line_items":[ + { + "name":"JOB_SUPPLIES", + "display_name":"Job supplies", + "value":8026, + "line_items":null + } + ] + }, + "gross_profit":41371, + "expenses":{ + "name":"OPERATING_EXPENSES", + "display_name":"Operating Expenses", + "value":0, + "line_items":[ + { + "name":"INSURANCE", + "display_name":"Insurance", + "value":0, + "line_items":null + }, + { + "name":"RENT_EXPENSE", + "display_name":"Rent", + "value":0, + "line_items":null + }, + { + "name":"UTILITIES", + "display_name":"Utilities", + "value":0, + "line_items":null + }, + { + "name":"EQUIPMENT", + "display_name":"Equipment & Tools", + "value":0, + "line_items":null + }, + { + "name":"ADVERTISING", + "display_name":"Advertising", + "value":0, + "line_items":null + }, + { + "name":"VEHICLE_EXPENSES", + "display_name":"Vehicle Expenses", + "value":0, + "line_items":null + } + ] + }, + "profit_before_taxes":41371, + "taxes":{ + "name":"TAXES", + "display_name":"Taxes", + "value":0, + "line_items":null + }, + "net_profit":41371, + "other_outflows":null, + "personal_expenses":null, + "fully_categorized":true + } +} +``` + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/reactivate-archived-plaid-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/reactivate-archived-plaid-account.mdx new file mode 100644 index 00000000000..bf3c74e13ae --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/reactivate-archived-plaid-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/plaid/accounts/{plaidAccountId}/reactivate +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/reactivate-custom-account-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/reactivate-custom-account-from-business.mdx new file mode 100644 index 00000000000..62358da6923 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/reactivate-custom-account-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/custom-accounts/{customAccountId}/reactivate +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/refunds.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/refunds.mdx new file mode 100644 index 00000000000..e6fc9945c81 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/refunds.mdx @@ -0,0 +1,69 @@ +--- +title: "Refunds object" +--- + +A refund represents a transaction that returns value to from a business to a customer. A specific payment can be refunded or a general refund can be applied to an invoice. + +### Attributes + + + Unique identifier for the refund. + + + Unique ID of the refund in your system for linking and idempotency. + + + Resource type. Value will be "Refund". + + + Timestamp when refund posted. + + + Amount of refund received by customer. + + + Fee, in cents, charged by payment processor for the refund. + + + Payment method. Possible values are: `CASH`, `CHECK`, `CREDIT_CARD`, `ACH`, `STORE_CREDIT`, + `OTHER` + + + Processor used to make the payment, if any. + Any processor name can be provided and will be tracked. + Supported processors (`STRIPE`, `SHOPIFY`) will have additional asset balance tracking. + + + Customer name of the recipient of the refund. + If invoice, line item, or payment ids are specified, they must match this recipient name. + + + Invoice ID this refund was applied to, if applicable. + This field does not need to be specified, but if `invoice_line_item_id` is also specified, it must belong to this invoice. + + + Invoice line item id specifying an exact product or service which was refunded. + + + Payment ID this refund was applied to, if applicable. + + + +```json Response + { + "data": { + "type": "Refund", + "id": "6195c7b0-acd6-4bcb-9417-57fabf5f772c", + "refunded_amount": 100, + "fee": 11, + "completed_at": "2024-03-25T01:46:59.309329Z", + "method": "STORE_CREDIT", + "processor": null, + "invoice_id": "905cbd23-db8a-4c9b-b5ea-05923eb88c23", + "invoice_line_item_id": "938cd7e1-0758-4965-96c1-724efd10825b", + "recipient_name": "John Doe" + }, + "meta": {} + } + ``` + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/remove-tag-from-transaction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/remove-tag-from-transaction.mdx new file mode 100644 index 00000000000..8e908d05a04 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/remove-tag-from-transaction.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /v1/businesses/{businessId}/bank-transactions/{transactionId}/tags/{tagId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/replace-custom-account-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/replace-custom-account-from-business.mdx new file mode 100644 index 00000000000..5b2f5af65a3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/replace-custom-account-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId}/custom-accounts/{customAccountId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/set-plaid-account-configuration.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/set-plaid-account-configuration.mdx new file mode 100644 index 00000000000..677279efb79 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/set-plaid-account-configuration.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId}/plaid/accounts/{plaidAccountId}/configuration +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/set-plaid-item-configuration.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/set-plaid-item-configuration.mdx new file mode 100644 index 00000000000..59c0a3a3f99 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/set-plaid-item-configuration.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId}/plaid/items/{plaidItemPlaidId}/configuration +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/summary-of-activities-for-all-businesses.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/summary-of-activities-for-all-businesses.mdx new file mode 100644 index 00000000000..59ef6b0c961 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/summary-of-activities-for-all-businesses.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/activity +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/summary-of-business-activities.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/summary-of-business-activities.mdx new file mode 100644 index 00000000000..7dc22b84b2e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/summary-of-business-activities.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v1/activity/businesses/{businessId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/uncategorize-transaction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/uncategorize-transaction.mdx new file mode 100644 index 00000000000..bf6c08cd879 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/uncategorize-transaction.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId}/bank-transactions/{transactionId}/uncategorize +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/unlink-and-archive-plaid-item-and-child-plaid-accounts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/unlink-and-archive-plaid-item-and-child-plaid-accounts.mdx new file mode 100644 index 00000000000..fcaa74d9b18 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/unlink-and-archive-plaid-item-and-child-plaid-accounts.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/plaid/items/{plaidItemPlaidId}/unlink +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-business.mdx new file mode 100644 index 00000000000..0874db1a462 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-custom-account-from-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-custom-account-from-business.mdx new file mode 100644 index 00000000000..dc5dbbb7fef --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-custom-account-from-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /v1/businesses/{businessId}/custom-accounts/{customAccountId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-ledger-account-on-business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-ledger-account-on-business.mdx new file mode 100644 index 00000000000..8ffd0171275 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-ledger-account-on-business.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v1/businesses/{businessId}/ledger/accounts/{accountId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-plaid-link-link.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-plaid-link-link.mdx new file mode 100644 index 00000000000..b92c0a84494 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/update-plaid-link-link.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/plaid/update-mode-link +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/upload-custom-account-transactions-by-json.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/upload-custom-account-transactions-by-json.mdx new file mode 100644 index 00000000000..8ac5290c89d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/api-reference/upload-custom-account-transactions-by-json.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v1/businesses/{businessId}/custom-accounts/{customAccountId} +--- \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/authentication.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/authentication.mdx new file mode 100644 index 00000000000..2238803e4d7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/authentication.mdx @@ -0,0 +1,46 @@ +--- +title: 'Authentication' +description: 'Authenticating your calls to the Layer API' +--- + +### Client credentials + +Layer uses OAuth2's client credentials flow to authenticate API clients. To start your development, we will give you a set of `client_id` and `client_secret` tokens. + + + To obtain a set of client credentials, reach out to our team [here](https://layerfi.com/#contact-form). + + + +### Getting a bearer token +Calls to the Layer API require a bearer access token. To receive an access token and make calls to other API endpoints, provide your `client_id` and `client_secret` in the body of a POST request to Layer’s authorization server as shown below. + +```bash +curl -X POST https://auth.layerfi.com/oauth2/token \ + -u : \ + -H "Content-Type: application/x-www-form-urlencoded" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "scope=https://sandbox.layerfi.com/sandbox" \ + --data-urlencode "client_id=" +``` + +The authorization server will respond with your granted access token. + +```json +{ + "access_token": "", + "expires_in": 3600, + "token_type": "Bearer" +} +``` + +### Making authenticated API calls + +Use the access token in requests to the API by including it as a Bearer token in the authorization header. + +```bash +curl https://sandbox.layerfi.com/whoami \ + -H "Authorization: Bearer " +``` + +Access tokens expire after 1 hour. To refresh your access token, make another call to Layer’s authorization endpoint with your `client_id` and `client_secret`. We recommend refreshing tokens for new sets of requests rather than persisting access tokens. \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/favicon.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/favicon.png new file mode 100644 index 00000000000..8ad52ae1e71 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/favicon.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/business-onboarding.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/business-onboarding.mdx new file mode 100644 index 00000000000..4aee912f07d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/business-onboarding.mdx @@ -0,0 +1,61 @@ +--- +title: 'Onboarding a Business' +description: 'Onboard one of your business customers to Layer' +--- + +The first step when using Layer is to onboard your customer to Layer using the [Create a business](/api-reference/create-new-business) endpoint. + +This endpoint creates the record for your customer in Layer’s systems. There are many optional features, such as specifying external accounts connected via Plaid. The [Create a business](/api-reference/create-new-business) endpoint lists the full set of options. + +```bash Request +curl -X POST https://sandbox.layerfi.com/v1/businesses \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "external_id": "test-acme-id", + "legal_name": "ACME LLC", + "tin": null, + "us_state": "CA", + "entity_type": "LLC", + "phone_number": "+18005555555", + "unit_ids": [{"unit_id": "111111"}] + }' +``` + + +### Plaid credentials +If you've added Plaid credentials (see [Plaid Configuration](/api-reference/plaid)), link Plaid accounts by including Plaid item ids & access tokens in the `plaid_items` field. +```json +"plaid_items": [ + { "item_id": "item-id-1", "access_token": "access_token_1" }, + { "item_id": "item-id-3", "access_token": "access_token_3" } +] +``` + +The Layer API will respond with the created [Business](/api-reference/business) object. Within the object will be a unique id, which you will use to make calls on behalf of the business in subsequent steps. + +```json Response +{ + "data": { + "id": "863ed926-e30d-40f4-8e7e-b0d5387ce4fb", + "type": "Business", + "external_id": "test-acme-id", + "legal_name": "ACME LLC", + "tin": null, + "business_activity_code": null, + "us_state": "CA", + "entityType": "LLC", + "phone_number": "+16504651359", + "imported_at": "2023-06-15T22:12:05.467940Z", + "updated_at": "2023-06-15T22:12:05.467940Z", + "archived_at": null, + "unit_accounts": [ + { + "id": "111111", + "imported_at": "2023-06-15T22:12:05.467940Z" + } + ] + }, + "meta": {} +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/embedded-components.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/embedded-components.mdx new file mode 100644 index 00000000000..529bf9aa4e0 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/embedded-components.mdx @@ -0,0 +1,213 @@ +--- +title: "Overview" +description: "Using Layer's prebuilt components to build bookkeeping directly into your app" +--- + +Layer publishes pre-built UI components to npm that can be dropped into any existing React app. These components handle all integration with Layer’s API with the exception of authentication, which must be done on your backend to avoid exposing credentials in your client-side apps. + +Currently available embedded components include: +* Bank Transaction Review and Categorization +* Profit & Loss Reports + * Month over month revenue & expense chart + * Monthly revenue & expense breakdowns + * Monthly expandable P&L table + +## React setup + + + + + The first step of using Layer's embedded components is to install the `layerfi/components` package via npm and your preferred package manager. + + ```bash + npm install @layerfi/components --save + ``` + + ```bash + yarn install @layerfi/components + ``` + + + Next, set up the `LayerProvider` context, which serves as the configuration for fetching data for an individual business. All individual UX components must be rendered within this context. + + **For local testing**, you can use your Layer staging credentials (`APP_ID` and `APP_SECRET`). + + ```tsx + import { LayerProvider } from "@layerfi/components"; + + + {...} + + ``` + + **In production**, you should use access tokens scoped to a specific business. + See the backend configuration section below for steps to retrieve scoped tokens: + + ```tsx + import { LayerProvider } from "@layerfi/components"; + + '} + > + {...} + + ``` + + + + **Option 1: Set primary colors in theme configuration** + + You can set a dark and light theme color for all components. + We recommend starting with simple customization like this using rgb, hsl or hex colors. + ```tsx + + {...} + + ``` + + **Option 2: Customize CSS variables** + + For more flexible customization, CSS variables are exposed to allow you to set colors and styles for the embedded components. + We recommend setting these variables within a scoped container to isolate the scope to Layer components. + In this example, we've set variables within the `.layer-container` class. + + + ```css + body .layer-container { + --color-black: #1a1a1a; + --color-white: white; + --color-neutral: #666666; + --color-neutral-50: #fafcfc; + --color-neutral-200: #eef0ef; + --color-neutral-700: #636665; + --color-red: #e46362; + --color-info-green: #29bc9b; + + --color-primary: var(--color-black); + --color-accent: var(--color-white); + --color-secondary: var(--color-neutral); + --color-success: var(--color-info-green); + --color-danger: var(--color-red); + --text-color-primary: var(--color-black); + --text-color-secondary: var(--color-neutral-700); + --bg-element-focus: var(--color-neutral-50); + + --font-family: "InterVariable", "Inter", sans-serif; + --font-family-numeric: "InterVariable", "Inter", sans-serif; + --text-sm: 12px; + --text-md: 14px; + --text-heading: 24px; + --font-weight-normal: 460; + --font-weight-bold: 580; + --spacing-sm: 12px; + --spacing-md: 16px; + --spacing-2xl: 36px; + --border-color: var(--color-neutral-200); + } + ``` + + + + Finally, add components to your pages as you would any React component. + + ```tsx + + ``` + + Some components have multiple sub components which can be optionally included for composition and layout customization. + + ```tsx + +
+ +
+ +
+ +
+ +
+ ``` + + Type definitions are available for all components to assist with discovering options & subcomponents. +
+ +
+ + + +## Backend setup + +To ensure an end user can only access their own data, the Layer component must be provided with a scoped access token which limits access to a specific business. This is a two step process. + + + + + Use your Layer-provided `client_id` and `client_secret` to obtain a platform-wide access token. See [Authentication](/authentication) for more information on this process. + This should be done server-side to avoid exposing your credentials in your client-side application. + + + + ```bash curl + curl -X POST https://auth.layerfi.com/oauth2/token \ + -u : \ + -H "Content-Type: application/x-www-form-urlencoded" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "scope=https://sandbox.layerfi.com/sandbox" \ + --data-urlencode "client_id=" + ``` + + ```tsx typescript + const clientId = ""; + const clientSecret = ""; + + const url = "https://auth.layerfi.com/oauth2/token"; + + axios.post(url, null, { + params: { + grant_type: "client_credentials", + scope: "https://sandbox.layerfi.com/sandbox", + client_id: clientId, + }, + auth: { + username: clientId, + password: clientSecret, + }, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); + ``` + + + + Finally, send this access token to the frontend and populate it in the `businessAccessToken` field of the `LayerProvider`context: + + ```tsx + + {...} + + ``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-bank-transactions.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-bank-transactions.mdx new file mode 100644 index 00000000000..6ca646fad64 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-bank-transactions.mdx @@ -0,0 +1,77 @@ +--- +title: 'Bank Accounts and Credit Cards' +--- + +Layer ingests data from your customers' business bank accounts and credit cards to incorporate into their accounting. Layer can connect to external customer accounts at traditional financial institutions as well as any embedded bank accounts and credit cards your platform already provides to customers. + +## External accounts +Layer leverages [Plaid](https://plaid.com/) to connect with your customers' accounts at external financial insitutions. + +### Using Layer's Plaid connection +If your platform does not already have a Plaid integration, you can utilize Layer's Plaid connection to import to your customers' external bank accounts and credit card data. Layer will manage all aspects of the Plaid integration on your behalf. + +To start importing customers' bank accounts and credit cards into Layer, embed Layer's _Bank Linking_ react component within your product. This component manages all aspects Layer's plaid connection and walks customers through the process of granting read-only access to their bank account and credit card activity. More detail on using Layer's embedded UI components is available [here](/guides/embedded-components). + +![Bank Linking Component](/images/bank-linking-component.png) + +Once your customers have connected their accounts, Layer will automatically start importing their bank account and credit card transactions. + +### Using your own Plaid connection +If your platform already has an integration with Plaid, you can grant Layer access to your existing Plaid data through Plaid's [processor token functionality](https://plaid.com/docs/api/processors/). Reach out to your Layer contact to set up this access. + +## Embedded accounts +If your platform provides an embedded bank account or credit card to your customers, you can pass this banking data to Layer as well. There are two ways to pass embedded banking activity to Layer: +* **API** - Pass bank and credit card transactions to Layer's API directly. +* **Layer integrations** - Layer has direct connections with top embedded banking platforms. You can grant Layer access to pull your customers' data from these platforms on your behalf. Layer currently supports: [Unit](https://www.unit.co/) and [Stripe Treasury](https://stripe.com/treasury). + +### Importing transactions via API +To import bank transactions to Layer, make a call to the [Import Bank Transactions](/api-reference/import-transactions) endpoint. This endpoint takes in the data on a bank transaction that is needed for Layer’s categorization and accounting logic. + +Note that the `bank_account_id` field should correspond to the `internal_bank_account_ids` field specified when [onboarding the Business](/guides/business-onboarding) as in the example below. + +```bash Request +curl -X POST https://sandbox.layerfi.com/v1/internal-bank-transactions \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '[{ + "external_id": 2093489, + "bank_account_id": "219384290", + "created_at": "2023-05-15T14:13:07Z", + "transaction_type": "Purchase", + "direction": "Debit", + "amount": 8026, + "balance": 40049, + "processed_counterparty_name": "HOME DEPOT", + "merchant_type": 5200, + "merchant_location": "HOUSTON, TX, US" + }]' +``` + +The API will respond with a list of ingested [Bank Transactions](/api-reference/bank-transaction). + +```json Response +{ + "data":[ + { + "type":"Flat_Bank_Transaction", + "id":"0e46b2b1-56b5-4ee4-bb5e-b0b708e50b47", + "bank_transaction_id":"2093489", + "created_at":"2023-05-15T14:13:07Z", + "transaction_type":"Purchase", + "bank_account_id":"219384290", + "business_id":"d2f6d97f-3345-4299-9ec2-468738c5d536", + "direction":"DEBIT", + "balance":40049, + "amount":8026, + "merchant_type":"5200", + "processed_counterparty_name":"HOME DEPOT", + "categorizationStatus":"PENDING" + } + ], + "meta":{ + } +} +``` + +### Using Layer's integrations +To leverage Layer's embedded banking integrations with [Unit](https://www.unit.co/) and [Stripe Treasury](https://stripe.com/treasury), reach out to your Layer conact. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-data-overview.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-data-overview.mdx new file mode 100644 index 00000000000..057d96651a6 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-data-overview.mdx @@ -0,0 +1,9 @@ +--- +title: 'Overview' +--- + +Once you have onboarded one of your customers to Layer by creating a [Business](/api-reference/business), you can start importing their financial data into Layer. This allows you to build the complete picture of financial activity to do their accounting. Layer can ingest the below types of data: +* [Invoices & Payments](/guides/importing-invoices) - The revenue collected by your business customers. This can be in-person sales made through a point of sale system, invoices sent through your software, ecommerce sales, or any other type of accounts receivable activity. +* [Bank Transactions](/guides/importing-bank-transactions) - Activity within your customers' business bank accounts and credit cards. +* **Payroll** - Payroll runs for paying employees and associated payroll taxes. Currently in Beta. Reach out to your Layer contact for access. +* **Bills** - Bills sent to your customers for their accounts payable obligations. Currently in Beta. Reach out to your Layer contact for access. \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-invoices.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-invoices.mdx new file mode 100644 index 00000000000..ca35995ce13 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/importing-invoices.mdx @@ -0,0 +1,242 @@ +--- +title: 'Invoices and Payments' +--- + +Invoices and Payments represent revenue collected by your business customers. This can be in-person sales made through a point of sale system, invoices sent through your software, ecommerce sales, or any other type of accounts receivable activity. + +To import invoices, make a call to the [Create Invoice](/api-reference/create-invoice-payments-from-batch) endpoint. This endpoint takes in the data on the sale your customer made, what was sold, any associated taxes, and how the sale was paid for. + +### Invoices without payments +Invoices can be created without any payments associated with them. Payments towards the invoice can then be recorded later on using the [Record an Invoice Payment](/api-reference/create-invoice-payments-from-batch) endpoint. + + +```bash Request +curl -X POST https://sandbox.layerfi.com/v1/businesses/:business_id/invoices \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "external_id": "019234", + "sent_at": "2024-04-02T09:02:00Z", + "due_at": "2023-04-02T09:02:00Z", + "invoice_number": "1", + "recipient_name": "John Doe", + "line_items": [ + { + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": 2, + "sales_taxes": [ + { + "tax_account": { + "type": "Tax_Name", + "name": "CALIFORNIA_VAT" + }, + "amount": 218 + } + ] + }, + { + "product": "Full drain cleaning service", + "unit_price": 25000, + "quantity": 1 + } + ], + "additional_discount": 250 + }' +``` + +The API will respond with an [Invoice](/api-reference/invoice) object. + +```json Response +{ + "data": { + "type": "Invoice", + "id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "business_id": "83d8fb80-31ee-4d57-b684-44b4aaa5e01f", + "external_id": "019234", + "status": "SENT", + "sent_at": "2024-04-02T09:02:00Z", + "due_at": "2023-04-02T09:02:00Z", + "paid_at": null, + "voided_at": null, + "invoice_number": "1", + "recipient_name": "John Doe", + "line_items": [ + { + "id": "e6a491dd-9c22-4403-a54f-32d741a7ec67", + "invoice_id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "account_identifier": null, + "description": null, + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": "2.00", + "subtotal": 2598, + "discount_amount": 0, + "sales_taxes_total": 218, + "sales_taxes": [ + { + "tax_account": { + "type": "Tax_Name", + "name": "CALIFORNIA_VAT" + }, + "amount": 218 + } + ], + "total_amount": 2816 + }, + { + "id": "44f06385-3ef5-4517-8095-eeedaf2054ab", + "invoice_id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "account_identifier": null, + "description": null, + "product": "Full drain cleaning service", + "unit_price": 25000, + "quantity": "1.00", + "subtotal": 25000, + "discount_amount": 0, + "sales_taxes_total": 0, + "total_amount": 25000 + } + ], + "subtotal": 27598, + "additional_discount": 250, + "additional_sales_taxes_total": 0, + "tips": 0, + "total_amount": 27566, + "outstanding_balance": 27566, + "payment_allocations": [], + "imported_at": "2024-04-19T02:23:59.902537Z", + "updated_at": null, + "transaction_tags": [] + } +} +``` + +### Invoices with payments +For instances where invoices are immediately paid, such as when using a point of sale device for in-person sales or online merchandise sales, you can also include payment information directly in the creation of the invoice. This looks very similar to the above example with the addition of payment information. + +```bash Request +curl -X POST https://sandbox.layerfi.com/v1/businesses/863ed926-e30d-40f4-8e7e-b0d5387ce4fb/invoices \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "external_id": "234988", + "sent_at": "2024-05-12T14:13:07Z", + "due_at": "2024-06-12T14:13:07Z", + "invoice_number": "2", + "recipient_name": "John Doe", + "line_items": [ + { + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": 1, + "sales_taxes": [ + { + "amount": 114 + } + ] + }, + { + "product": "Cleanout snake", + "unit_price": 18000, + "quantity": 1 + } + ], + "additional_sales_taxes": [ + { + "amount": 1291 + } + ], + "payments": [ + { + "external_id": "239872", + "method": "CREDIT_CARD", + "amount": 20704, + "processor": "MY_PROCESSOR" + } + ] + }' +``` + +In this case, the API will respond with the same created invoice, but with the invoice marked as fully or partially paid and the payment information included. + +```json Response +{ + "data": { + "type": "Invoice", + "id": "d01c9839-0378-4d44-b409-a0ae1e5dbb59", + "business_id": "83d8fb80-31ee-4d57-b684-44b4aaa5e01f", + "external_id": "234988", + "status": "PAID", + "sent_at": "2024-05-12T14:13:07Z", + "due_at": "2024-06-12T14:13:07Z", + "paid_at": "2024-04-19T02:24:00.009658Z", + "voided_at": null, + "invoice_number": "2", + "recipient_name": "John Doe", + "line_items": [ + { + "id": "fd60aa16-a0a6-40de-a814-b01836acfd36", + "invoice_id": "d01c9839-0378-4d44-b409-a0ae1e5dbb59", + "account_identifier": null, + "description": null, + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": "1.00", + "subtotal": 1299, + "discount_amount": 0, + "sales_taxes_total": 114, + "sales_taxes": [ + { + "tax_account": { + "type": "Legder_Account_Id", + "id": "ba1a5e91-d04a-4c67-919e-f09a20d6e151" + }, + "amount": 114 + } + ], + "total_amount": 1413 + }, + { + "id": "4dd7708a-cad4-46e6-b5ff-34248a0b141e", + "invoice_id": "d01c9839-0378-4d44-b409-a0ae1e5dbb59", + "account_identifier": null, + "description": null, + "product": "Cleanout snake", + "unit_price": 18000, + "quantity": "1.00", + "subtotal": 18000, + "discount_amount": 0, + "sales_taxes_total": 0, + "total_amount": 18000 + } + ], + "subtotal": 19299, + "additional_discount": 0, + "additional_sales_taxes_total": 1291, + "additional_sales_taxes": [ + { + "tax_account": { + "type": "Legder_Account_Id", + "id": "ba1a5e91-d04a-4c67-919e-f09a20d6e151" + }, + "amount": 1291 + } + ], + "tips": 0, + "total_amount": 20704, + "outstanding_balance": 0, + "payment_allocations": [ + { + "invoice_id": "d01c9839-0378-4d44-b409-a0ae1e5dbb59", + "payment_id": "4d769e05-a101-4a16-8de5-6c37fbcec088", + "amount": 20704, + "transaction_tags": [] + } + ], + "imported_at": "2024-04-19T02:24:00.009658Z", + "updated_at": null, + "transaction_tags": [] + } +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/offering-accounting-overview.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/offering-accounting-overview.mdx new file mode 100644 index 00000000000..7f6ac52ed5b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/offering-accounting-overview.mdx @@ -0,0 +1,7 @@ +--- +title: 'Overview' +--- + +Once you have [imported](/guides/importing-data-overview) your customers' financial data into Layer, you can start offering them Layer's full suite of accounting features within your platform. This includes: +* [Transaction Categorization](/guides/transaction-categorization) - Categorize customer bank transactions into revenue/expense categories and automatically reconcile bank transactions with revenue, payroll, and bill data. This ensures customers' financial data is properly recorded in their general ledger. +* [Accounting Reports](/guides/reporting) - Surface real-time financial reports such as the profit and loss statement, balance sheet, or AR/AP aging summary. \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/reporting.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/reporting.mdx new file mode 100644 index 00000000000..15528e1d6e4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/reporting.mdx @@ -0,0 +1,145 @@ +--- +title: 'Accounting Reports' +--- + +Layer provides a full set of accounting charts and table reports that you can embed directly within your platform. You can use these reports to give your customers a clear picture of their business profitability, cash flows, and balance sheet. + +There are two ways to offer reporting within your platform: +* **Embedded UI components** - Embed Layer's charts and report UI components within your experience. +* **API** - Call Layer's API directly to build your own accounting reports. + + +## Embedded components +Layer provides a variety of chart and table report UI components that you can download via npm and embed directly within your frontend. Below are some examples: + +**Profit & Loss chart** +![Profit & Loss chart](/images/pnl-chart.png) + +**Profit & Loss table** +![Profit & Loss table](/images/pnl-table.png) + +More detail on using Layer's embedded UI components is available [here](/guides/embedded-components). Reach out to your Layer contact for a full list of embedded reporting components. + +## API + +### Retrieving a Profit & Loss report +To build your own Profit & Loss report UI make a call to the [retrieve a Profit and Loss Report](/api-reference/get-profit-and-loss-report-for-business) endpoint. + +The start_date query string parameter is required for all calls to the Retrieve a Profit and Loss Report endpoint. + + +```bash Request +curl -X GET https://sandbox.layerfi.com/v1/businesses/:business_id/reports/profit-and-loss?start_date=2023-01-01T00:00:00-06:00&end_date=2023-12-01T00:00:00-06:00 + -H "Authorization: Bearer " +``` + +The API will respond with the generated [Profit and Loss Report](/api-reference/pnl). + +```json Response +{ + "data":{ + "type":"Profit_And_Loss", + "business_id":"d2f6d97f-3345-4299-9ec2-468738c5d536", + "start_date":"2023-01-01T06:00:00Z", + "end_date":"2023-12-01T06:00:00Z", + "income":{ + "name":"REVENUE", + "display_name":"Revenue", + "value":49397, + "line_items":[ + { + "name":"SERVICES_REVENUE", + "display_name":"Service Revenue", + "value":46897, + "line_items":null + }, + { + "name":"GOODS_REVENUE", + "display_name":"Sale of Goods Revenue", + "value":0, + "line_items":null + }, + { + "name":"DISCOUNTS", + "display_name":"Discounts & Refunds", + "value":2500, + "line_items":null + } + ] + }, + "cost_of_goods_sold":{ + "name":"COGS", + "display_name":"Cost of Goods Sold", + "value":8026, + "line_items":[ + { + "name":"JOB_SUPPLIES", + "display_name":"Job supplies", + "value":8026, + "line_items":null + } + ] + }, + "gross_profit":41371, + "expenses":{ + "name":"OPERATING_EXPENSES", + "display_name":"Operating Expenses", + "value":0, + "line_items":[ + { + "name":"INSURANCE", + "display_name":"Insurance", + "value":0, + "line_items":null + }, + { + "name":"RENT_EXPENSE", + "display_name":"Rent", + "value":0, + "line_items":null + }, + { + "name":"UTILITIES", + "display_name":"Utilities", + "value":0, + "line_items":null + }, + { + "name":"EQUIPMENT", + "display_name":"Equipment & Tools", + "value":0, + "line_items":null + }, + { + "name":"ADVERTISING", + "display_name":"Advertising", + "value":0, + "line_items":null + }, + { + "name":"VEHICLE_EXPENSES", + "display_name":"Vehicle Expenses", + "value":0, + "line_items":null + } + ] + }, + "profit_before_taxes":41371, + "taxes":{ + "name":"TAXES", + "display_name":"Taxes", + "value":0, + "line_items":null + }, + "net_profit":41371, + "other_outflows":null, + "personal_expenses":null, + "fully_categorized":true + } +} +``` + +You can use this data to build your own profit and loss report UI within your platform. + +### Supported reports +A full list of supported reports is available in the [API reference](/api-reference/pnl) \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/transaction-categorization.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/transaction-categorization.mdx new file mode 100644 index 00000000000..cf934d28de4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/guides/transaction-categorization.mdx @@ -0,0 +1,173 @@ +--- +title: 'Transaction Categorization' +--- + +In order to get an accurate picture of their accounting, your customers must categorize and reconcile their bank account and credit card activity. This ensures all banking activity is recorded into the correct revenue and expense categories and matched with any other revenue, payroll, or bill data that has been imported into Layer. + +Transaction categorization is one of the most common workflows performed within accounting software and will usually be done by either your customer or a bookkeeper hired by your customer. + +Layer's embedded accounting platform allows your customers to complete their transaction categorization workflows directly within your platform. There are two ways to offer transaction categorization within your platform: +* **Embedded UI component** - Embed Layer's _Transaction List_ UI component within your experience. +* **API** - Build a customized transaction categorization workflows on top of Layer's transaction categorization API. + + +## Embedded component +Install Layer's Transaction List UI component via npm and embed it directly within your frontend. The Transaction List component handles all workflows related to transaction categorization including: displaying the list of transactions to categorize, surfacing suggested categorizations, matching transactions with invoices, and displaying the list of already categorized transactions. More detail on using Layer's embedded UI components is available [here](/guides/embedded-components). + +![Transaction List Component](/images/bank-transactions-component.png) + + +## API +For full control over the transaction categorization experience, you can build all transaction categorization workflows on top of Layer's API. + +### Retrieving categorized transactions +The first step of transaction categorization is to retrieve Layer’s assigned categories and recommended categorization flows for all bank account and credit card transactions that have been imported into Layer. This is done by making a call to the [List all Bank Transactions](/api-reference/bank-transactions/list) endpoint. + +```bash Request +curl https://sandbox.layerfi.com/v1/businesses/:business_id/bank-transactions \ + -H "Authorization: Bearer " +``` + +The API will respond with a list of all imported transactions. + +```json Response +{ + "data": [ + { + "type": "Bank_Transaction", + "id": "0e46b2b1-56b5-4ee4-bb5e-b0b708e50b47", + "business_id": "d2f6d97f-3345-4299-9ec2-468738c5d536", + "source": "UNIT", + "source_transaction_id": "2093489", + "source_account_id": "219384290", + "imported_at": "2023-12-15T05:45:06.150088Z", + "date": "2023-05-15T14:13:07Z", + "direction": "DEBIT", + "amount": 8026, + "counterparty_name": "HOME DEPOT", + "description": null, + "categorization_status": "READY_FOR_INPUT", + "category": null, + "categorization_method": null, + "categorization_flow": { + "type": "ASK_FROM_SUGGESTIONS", + "suggestions": [ + { + "type": "Account", + "id": "885a3a95-fe3c-40fb-8424-86c44b7def15", + "stable_name": "JOB_SUPPLIES", + "category": "JOB_SUPPLIES", + "display_name": "Job supplies" + }, + { + "type": "Account", + "id": "2b3e300a-0fe8-4581-bfa5-c5a74dae0a7f", + "stable_name": "EQUIPMENT", + "category": "EQUIPMENT", + "display_name": "Equipment & Tools" + }, + { + "type": "Account", + "id": "3015ef8d-42aa-485e-90a9-3263f55155bc", + "stable_name": "VEHICLE_EXPENSES", + "category": "VEHICLE_EXPENSES", + "display_name": "Vehicle Expenses" + } + ] + }, + "projected_income_category": "EXPENSE" + } + ], + "meta": { + "pagination": { + "sort_by": "date", + "sort_order": "ASC", + "cursor": null, + "has_more": false + } + } +} +``` + +Each Bank Transaction in the returned list will include two fields: +- `category` is the journaled category that has been assigned to the transaction and determines how the transaction will be journaled to the general ledger of the business in Layer’s systems. When Layer has high confidence in the category of a transaction, this will be set automatically. It can also be populated using input from the end user on your platform. +- `categorization_flow` is Layer's recommended approach for categorizing the transaction. In some cases, this will be automatically categorizing the transaction without any human input. In other cases, Layer will recommend prompting the end user to select from a list of suggested categories. + +### Categorizing transactions +Transactions can be categorized via API any time after they’ve been imported, regardless of whether the business has SMS based categorized flows turned on or off. + +Transactions are categorized one at a time with a `PUT` request to the [Categorize Bank Transaction](/api-reference/bank-transaction/categorize) endpoint. Transactions can be recategorized after an original categorization is assigned, however this will change reports for any time period including the transaction. + +```bash Request +curl -X PUT https://sandbox.layerfi.com/v1/businesses/:business_id/bank-transactions/:transaction_id/categorize \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "type": "Category", + "category": { + "type": "StableName", + "stable_name": "JOB_SUPPLIES" + } +}' +``` + +The API will respond with a single [Bank Transaction](/api-reference/bank-transaction/bank-transaction) object. + +```json Response +{ + "data":{ + "type":"Bank_Transaction", + "id":"0e46b2b1-56b5-4ee4-bb5e-b0b708e50b47", + "business_id":"d2f6d97f-3345-4299-9ec2-468738c5d536", + "source":"UNIT", + "source_transaction_id":"2093489", + "source_account_id":"219384290", + "imported_at":"2023-12-15T05:45:06.150088Z", + "date":"2023-05-15T14:13:07Z", + "direction":"DEBIT", + "amount":8026, + "counterparty_name":"HOME DEPOT", + "description":null, + "categorization_status":"CATEGORIZED", + "category":{ + "type":"Account", + "id":"885a3a95-fe3c-40fb-8424-86c44b7def15", + "stable_name":"JOB_SUPPLIES", + "category":"JOB_SUPPLIES", + "display_name":"Job supplies" + }, + "categorization_method":"API", + "categorization_flow":{ + "type":"ASK_FROM_SUGGESTIONS", + "suggestions":[ + { + "type":"Account", + "id":"885a3a95-fe3c-40fb-8424-86c44b7def15", + "stable_name":"JOB_SUPPLIES", + "category":"JOB_SUPPLIES", + "display_name":"Job supplies" + }, + { + "type":"Account", + "id":"2b3e300a-0fe8-4581-bfa5-c5a74dae0a7f", + "stable_name":"EQUIPMENT", + "category":"EQUIPMENT", + "display_name":"Equipment & Tools" + }, + { + "type":"Account", + "id":"3015ef8d-42aa-485e-90a9-3263f55155bc", + "stable_name":"VEHICLE_EXPENSES", + "category":"VEHICLE_EXPENSES", + "display_name":"Vehicle Expenses" + } + ] + }, + "projected_income_category":"EXPENSE" + } +} +``` + +The `categorization_method` field will be populated to identify how the category was set (ex. “API”) + +Even after categorization, the `categorization_flow` and nested `suggestions` array are retained on the [Bank Transaction](/api-reference/bank-transaction/bank-transaction) object so that they can be re-displayed in a UI if a user wants to recategorize. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/bank-linking-component.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/bank-linking-component.png new file mode 100644 index 00000000000..4aa5048e210 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/bank-linking-component.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/bank-transactions-component.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/bank-transactions-component.png new file mode 100644 index 00000000000..b78526bef0a Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/bank-transactions-component.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/checks-passed.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/checks-passed.png new file mode 100644 index 00000000000..3303c773646 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/checks-passed.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/hero-dark.svg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/hero-dark.svg new file mode 100644 index 00000000000..59ab0975814 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/hero-dark.svg @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/hero-light.svg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/hero-light.svg new file mode 100644 index 00000000000..9db54d9c078 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/hero-light.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/pnl-chart.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/pnl-chart.png new file mode 100644 index 00000000000..c39a2b90a7a Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/pnl-chart.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/pnl-table.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/pnl-table.png new file mode 100644 index 00000000000..7af702b7cc2 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/images/pnl-table.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/introduction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/introduction.mdx new file mode 100644 index 00000000000..a6727e0c125 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/introduction.mdx @@ -0,0 +1,21 @@ +--- +title: Overview +--- + +The Layer API provides access to Layer's embeddable accounting software. Using the API, you can seamlessly pass your customers' financial data into Layer's accounting software and surface a complete SMB accounting product within your platform. + +## API Environments + +The Layer API has two environments: +- **Sandbox:** `sandbox.layerfi.com` - Test environment for building your integration with Layer. +- **Production:** `api.layerfi.com` - Production environment for live usage with your SMB customers. + +## Getting an API key + +The first step to getting started is to contact us to obtain an API key. We will first send you a sandbox API key to use during development. Once you feel ready to go live and we review your integration, we'll provide your production API key. + +To obtain your API key and get started, reach out to our team [here](https://layerfi.com/#contact-form). + +## Getting started + +If you're just getting started, read about authenticating to our API and then check out our guides. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/logo/layer_logo.svg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/logo/layer_logo.svg new file mode 100644 index 00000000000..3081990d535 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/logo/layer_logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/mint.json b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/mint.json new file mode 100644 index 00000000000..e2b81c2b050 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/layerfi/mint.json @@ -0,0 +1,240 @@ +{ + "$schema": "https://mintlify.com/schema.json", + "name": "Layer", + "logo": { + "dark": "/logo/layer_logo.svg", + "light": "/logo/layer_logo.svg", + "href": "http://layerfi.com" + }, + "favicon": "/favicon.png", + "colors": { + "primary": "#5ba183", + "light": "#7e9be5", + "dark": "#0c48e5", + "background": { + "dark": "#090014" + }, + "anchors": { + "from": "#FF7F57", + "to": "#9563FF" + } + }, + "modeToggle": { + "default": "light", + "isHidden": true + }, + "topbarLinks": [], + "tabs": [ + { + "name": "API Reference", + "url": "api-reference" + } + ], + "anchors": [], + "navigation": [ + { + "group": "Get Started", + "pages": ["introduction", "authentication", "guides/business-onboarding"] + }, + { + "group": "Importing Financial Data", + "pages": [ + "guides/importing-data-overview", + "guides/importing-invoices", + "guides/importing-bank-transactions" + ] + }, + { + "group": "Offering Accounting", + "pages": [ + "guides/offering-accounting-overview", + "guides/transaction-categorization", + "guides/reporting" + ] + }, + { + "group": "Embedded Components", + "pages": [ + "guides/embedded-components" + ] + }, + { + "group": "Businesses", + "pages": [ + "api-reference/business", + "api-reference/list-businesses", + "api-reference/create-new-business", + "api-reference/get-business", + "api-reference/update-business", + "api-reference/archive-business", + "api-reference/list-chart-of-accounts-categories-for-business", + { + "group": "Plaid Configuration", + "pages": [ + "api-reference/list-plaid-items-for-business", + "api-reference/delete-plaid-item-from-business", + "api-reference/get-plaid-item-configuration", + "api-reference/set-plaid-item-configuration", + "api-reference/unlink-and-archive-plaid-item-and-child-plaid-accounts", + "api-reference/create-plaid-item-link-token", + "api-reference/exchange-plaid-token-and-add-item-to-business", + "api-reference/update-plaid-link-link", + "api-reference/list-plaid-accounts-for-business", + "api-reference/archive-plaid-account", + "api-reference/reactivate-archived-plaid-account", + "api-reference/get-plaid-account-configuration", + "api-reference/set-plaid-account-configuration" + ] + } + ] + }, + { + "group": "Financial Activities", + "pages": [ + { + "group": "Transactions", + "pages": [ + "api-reference/bank-transaction", + "api-reference/import-transactions", + "api-reference/list-transactions-from-business", + "api-reference/get-transaction-from-business", + "api-reference/categorize-transaction", + "api-reference/uncategorize-transaction", + "api-reference/match-transaction", + "api-reference/bulk-match-transactions", + "api-reference/list-transaction-ledger-entries" + ] + }, + { + "group": "Payouts", + "pages": [ + "api-reference/list-payouts-from-business", + "api-reference/get-payout-from-business" + ] + }, + { + "group": "Accounts Receivable", + "pages": [ + { + "group": "Invoices", + "pages": [ + "api-reference/invoice", + "api-reference/get-invoice-from-business", + "api-reference/create-invoice-payments-from-batch", + "api-reference/delete-invoice-from-business" + ] + }, + { + "group": "Payments", + "pages": [ + "api-reference/payments", + "api-reference/list-invoice-payments-from-business", + "api-reference/create-invoice-payments-from-batch", + "api-reference/get-invoice-payment-from-business", + "api-reference/delete-invoice-payment-from-business" + ] + }, + { + "group": "Refunds", + "pages": [ + "api-reference/refunds", + "api-reference/list-refunds-from-business" + ] + } + ] + } + ] + }, + { + "group": "Reporting", + "pages": [ + "api-reference/pnl", + "api-reference/get-profit-and-loss-report-for-business", + "api-reference/get-profit-and-loss-entries-from-business", + "api-reference/download-ledger-transactions-from-business-as-csv" + ] + }, + { + "group": "General Ledger", + "pages": [ + "api-reference/ledger", + "api-reference/list-account-balances-from-ledger", + "api-reference/get-chart-of-accounts-for-business", + "api-reference/get-ledger-from-business", + "api-reference/list-ledger-entries-from-business", + "api-reference/get-ledger-entry-from-business", + "api-reference/create-reversal-entry-for-ledger-entry", + "api-reference/create-manual-ledger-entries", + "api-reference/get-or-create-ledger-account-on-business", + "api-reference/list-ledger-account-line-items" + ] + }, + { + "group": "External Accounts", + "pages": [ + "api-reference/list-linked-external-accounts-from-business", + "api-reference/get-linked-external-account-from-business", + "api-reference/get-transactions-from-linked-external-account", + "api-reference/get-opening-balance-from-linked-external-account", + "api-reference/list-balance-timestamps-from-linked-external-account", + "api-reference/archive-linked-external-account", + { + "group": "Custom Accounts", + "pages": [ + "api-reference/get-custom-account-from-business", + "api-reference/add-custom-account-for-business", + "api-reference/archive-custom-account-from-business", + "api-reference/reactivate-custom-account-from-business", + "api-reference/create-custom-account-balance-timestamp", + "api-reference/calculate-new-custom-account-opening-balance-entry" + ] + } + ] + }, + { + "group": "Tags", + "pages": [ + "api-reference/list-tags-applied-to-transaction", + "api-reference/apply-list-of-tags-to-list-of-transactions", + "api-reference/remove-tag-from-transaction", + "api-reference/list-tags-applied-to-payout", + "api-reference/apply-list-of-tags-to-list-of-payouts", + "api-reference/list-tags-applied-to-invoice", + "api-reference/apply-list-of-tags-to-list-of-invoices", + "api-reference/list-tags-applied-to-invoice-payment", + "api-reference/list-tags-applied-to-invoice-payment-allocation", + "api-reference/apply-list-of-tags-to-list-of-invoice-payment-allocations", + "api-reference/delete-tags-applied-to-payment-allocation" + ] + }, + { + "group": "Client Admin", + "pages": [ + "api-reference/get-client-info", + "api-reference/get-business-auth-token", + "api-reference/summary-of-activities-for-all-businesses", + "api-reference/summary-of-business-activities", + { + "group": "Configuration", + "pages": [ + "api-reference/plaid", + "api-reference/get-plaid-client-id-and-secret", + "api-reference/configure-plaid-client-id-and-secret", + "api-reference/get-stripe-secret", + "api-reference/configure-stripe-secret" + ] + } + ] + } + ], + "api": { + "baseUrl": "https://sandbox.layerfi.com", + "auth": { + "method": "bearer" + }, + "playground": { + "mode": "simple" + } + }, + "openapi": ["/OpenApiSpec.yaml"] +} diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/README.md b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/README.md new file mode 100644 index 00000000000..cfed42991b5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/README.md @@ -0,0 +1,32 @@ +# Mintlify Starter Kit + +Click on `Use this template` to copy the Mintlify starter kit. The starter kit contains examples including + +- Guide pages +- Navigation +- Customizations +- API Reference pages +- Use of popular components + +### Development + +Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) to preview the documentation changes locally. To install, use the following command + +``` +npm i -g mintlify +``` + +Run the following command at the root of your documentation (where mint.json is) + +``` +mintlify dev +``` + +### Publishing Changes + +Install our Github App to autopropagate changes from youre repo to your deployment. Changes will be deployed to production automatically after pushing to the default branch. Find the link to install on your dashboard. + +#### Troubleshooting + +- Mintlify dev isn't running - Run `mintlify install` it'll re-install dependencies. +- Page loads as a 404 - Make sure you are running in a folder with `mint.json` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/creates-a-new-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/creates-a-new-account.mdx new file mode 100644 index 00000000000..690f804ad0f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/creates-a-new-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /accounts +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/deletes-a-member-from-an-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/deletes-a-member-from-an-account.mdx new file mode 100644 index 00000000000..5790d2d7b47 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/deletes-a-member-from-an-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /accounts/{accountId}/members/{member_uuid} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/invites-a-new-member-to-an-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/invites-a-new-member-to-an-account.mdx new file mode 100644 index 00000000000..d99bfbecc96 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/account/invites-a-new-member-to-an-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /accounts/{accountId}/members +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/authentication/authenticates-with-an-external-authentication-provider-for-a-users-account.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/authentication/authenticates-with-an-external-authentication-provider-for-a-users-account.mdx new file mode 100644 index 00000000000..e7c490adecd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/authentication/authenticates-with-an-external-authentication-provider-for-a-users-account.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /authenticate +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/creates-a-new-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/creates-a-new-documentcollection.mdx new file mode 100644 index 00000000000..dc2cbda10a4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/creates-a-new-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /collections/{collectionName} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/creates-an-index-for-a-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/creates-an-index-for-a-documentcollection.mdx new file mode 100644 index 00000000000..4c8cf0b5cc2 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/creates-an-index-for-a-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /collections/{collectionName}/index/create +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/deletes-a-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/deletes-a-documentcollection.mdx new file mode 100644 index 00000000000..c57777d2597 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/deletes-a-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /collections/{collectionName} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/gets-a-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/gets-a-documentcollection.mdx new file mode 100644 index 00000000000..aaeb49c2e07 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/gets-a-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /collections/{collectionName} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/gets-a-list-of-documentcollections.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/gets-a-list-of-documentcollections.mdx new file mode 100644 index 00000000000..e4275fd9ece --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/gets-a-list-of-documentcollections.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /collections +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/updates-a-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/updates-a-documentcollection.mdx new file mode 100644 index 00000000000..b2dcb3c1c30 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/collection/updates-a-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /collections/{collectionName} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-deletes-documents-from-a-documentcollection-by-uuid.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-deletes-documents-from-a-documentcollection-by-uuid.mdx new file mode 100644 index 00000000000..cdea43e9fc1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-deletes-documents-from-a-documentcollection-by-uuid.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /collections/{collectionName}/documents/batchDelete +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-gets-documents-from-a-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-gets-documents-from-a-documentcollection.mdx new file mode 100644 index 00000000000..bdc7fd7b41d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-gets-documents-from-a-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /collections/{collectionName}/documents/batchGet +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-updates-documents-in-a-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-updates-documents-in-a-documentcollection.mdx new file mode 100644 index 00000000000..ff50d504e7e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/batch-updates-documents-in-a-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /collections/{collectionName}/documents/batchUpdate +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/creates-multiple-documents-in-a-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/creates-multiple-documents-in-a-documentcollection.mdx new file mode 100644 index 00000000000..e5588beb5a9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/creates-multiple-documents-in-a-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /collections/{collectionName}/document +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/delete-document-from-a-documentcollection-by-uuid.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/delete-document-from-a-documentcollection-by-uuid.mdx new file mode 100644 index 00000000000..93203a57ceb --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/delete-document-from-a-documentcollection-by-uuid.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /collections/{collectionName}/documents/uuid/{documentUUID} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/gets-a-document-from-a-documentcollection-by-uuid.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/gets-a-document-from-a-documentcollection-by-uuid.mdx new file mode 100644 index 00000000000..32c77e9e65e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/gets-a-document-from-a-documentcollection-by-uuid.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /collection/{collectionName}/documents/uuid/{documentUUID} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/searches-documents-in-a-documentcollection.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/searches-documents-in-a-documentcollection.mdx new file mode 100644 index 00000000000..0a4c17ef7cd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/searches-documents-in-a-documentcollection.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /collections/{collectionName}/search +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/updates-a-document-in-a-documentcollection-by-uuid.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/updates-a-document-in-a-documentcollection-by-uuid.mdx new file mode 100644 index 00000000000..67b86edcc6a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/document/updates-a-document-in-a-documentcollection-by-uuid.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /api/v2/collections/{collectionName}/documents/uuid/{documentUUID} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/add-memory-messages-to-a-given-session.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/add-memory-messages-to-a-given-session.mdx new file mode 100644 index 00000000000..661f92cc27e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/add-memory-messages-to-a-given-session.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /sessions/{sessionId}/memory +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/delete-memory-messages-for-a-given-session.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/delete-memory-messages-for-a-given-session.mdx new file mode 100644 index 00000000000..e78ea33ad3c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/delete-memory-messages-for-a-given-session.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /sessions/{sessionId}/memory +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/returns-a-memory-latest-summary-and-list-of-messages-for-a-given-session.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/returns-a-memory-latest-summary-and-list-of-messages-for-a-given-session.mdx new file mode 100644 index 00000000000..fb019864bb3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/returns-a-memory-latest-summary-and-list-of-messages-for-a-given-session.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /sessions/{sessionId}/memory +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/synthesize-a-question-for-a-given-session.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/synthesize-a-question-for-a-given-session.mdx new file mode 100644 index 00000000000..c89cd7605b4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/memory/synthesize-a-question-for-a-given-session.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /sessions/{sessionId}/synthesize_question +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/retrieves-a-specific-message.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/retrieves-a-specific-message.mdx new file mode 100644 index 00000000000..d1ac339c8c0 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/retrieves-a-specific-message.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /sessions/{sessionId}/messages/{messageId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/retrieves-all-messages-for-a-specific-session.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/retrieves-all-messages-for-a-specific-session.mdx new file mode 100644 index 00000000000..2ed126a9987 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/retrieves-all-messages-for-a-specific-session.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /sessions/{sessionId}/messages +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/updates-the-metadata-of-a-specific-message.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/updates-the-metadata-of-a-specific-message.mdx new file mode 100644 index 00000000000..e874d677a86 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/messages/updates-the-metadata-of-a-specific-message.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /sessions/{sessionId}/messages/{messageId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/openapi.json b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/openapi.json new file mode 100644 index 00000000000..8a113c1769c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/openapi.json @@ -0,0 +1,1592 @@ +{ + "openapi": "3.0.1", + "info": { "title": "Zep Cloud API", "contact": {}, "version": "0.x" }, + "servers": [{ "url": "https://app.getzep.com/api/v2" }], + "paths": { + "/api/v2/collections/{collectionName}/documents/uuid/{documentUUID}": { + "patch": { + "tags": ["document"], + "summary": "Updates a Document in a DocumentCollection by UUID", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "documentUUID", + "in": "path", + "description": "UUID of the Document to be updated", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Document to be updated", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.UpdateDocumentRequest" } } + }, + "required": true + }, + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "document" + } + }, + "/collection/{collectionName}/documents/uuid/{documentUUID}": { + "get": { + "tags": ["document"], + "summary": "Gets a Document from a DocumentCollection by UUID", + "description": "Returns specified Document from a DocumentCollection.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "documentUUID", + "in": "path", + "description": "UUID of the Document to be updated", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.DocumentResponse" } } } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + }, + "/collections": { + "get": { + "tags": ["collection"], + "summary": "Gets a list of DocumentCollections", + "description": "Returns a list of all DocumentCollections.", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "array", + "items": { "$ref": "#/components/schemas/models.DocumentCollectionResponse" } + } + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + }, + "/collections/{collectionName}": { + "get": { + "tags": ["collection"], + "summary": "Gets a DocumentCollection", + "description": "Returns a DocumentCollection if it exists.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.DocumentCollectionResponse" } } + } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + }, + "post": { + "tags": ["collection"], + "summary": "Creates a new DocumentCollection", + "description": "If a collection with the same name already exists, an error will be returned.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Document Collection", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.CreateDocumentCollectionRequest" } } + }, + "required": true + }, + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "collection" + }, + "delete": { + "tags": ["collection"], + "summary": "Deletes a DocumentCollection", + "description": "If a collection with the same name already exists, it will be overwritten.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + }, + "patch": { + "tags": ["collection"], + "summary": "Updates a DocumentCollection", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Document Collection", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.UpdateDocumentCollectionRequest" } } + }, + "required": true + }, + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "collection" + } + }, + "/collections/{collectionName}/document": { + "post": { + "tags": ["document"], + "summary": "Creates Multiple Documents in a DocumentCollection", + "description": "Creates Documents in a specified DocumentCollection and returns their UUIDs.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Array of Documents to be created", + "content": { + "application/json": { + "schema": { "type": "array", "items": { "$ref": "#/components/schemas/models.CreateDocumentRequest" } } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "documents" + } + }, + "/collections/{collectionName}/documents/batchDelete": { + "post": { + "tags": ["document"], + "summary": "Batch Deletes Documents from a DocumentCollection by UUID", + "description": "Deletes specified Documents from a DocumentCollection.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "UUIDs of the Documents to be deleted", + "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } }, + "required": true + }, + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "documentUUIDs" + } + }, + "/collections/{collectionName}/documents/batchGet": { + "post": { + "tags": ["document"], + "summary": "Batch Gets Documents from a DocumentCollection", + "description": "Returns Documents from a DocumentCollection specified by UUID or ID.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "UUIDs and IDs of the Documents to be fetched", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.GetDocumentListRequest" } } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "type": "array", "items": { "$ref": "#/components/schemas/models.DocumentResponse" } } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "documentRequest" + } + }, + "/collections/{collectionName}/documents/batchUpdate": { + "patch": { + "tags": ["document"], + "summary": "Batch Updates Documents in a DocumentCollection", + "description": "Updates Documents in a specified DocumentCollection.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Array of Documents to be updated", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/models.UpdateDocumentListRequest" } + } + } + }, + "required": true + }, + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "documents" + } + }, + "/collections/{collectionName}/documents/uuid/{documentUUID}": { + "delete": { + "tags": ["document"], + "summary": "Delete Document from a DocumentCollection by UUID", + "description": "Delete specified Document from a DocumentCollection.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "documentUUID", + "in": "path", + "description": "UUID of the Document to be deleted", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "404": { + "description": "Document Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + }, + "/collections/{collectionName}/index/create": { + "post": { + "tags": ["collection"], + "summary": "Creates an index for a DocumentCollection", + "description": "Creates an index for the specified DocumentCollection to improve query performance.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "force", + "in": "query", + "description": "Force index creation, even if there are too few documents to index", + "schema": { "type": "boolean" } + } + ], + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + }, + "/collections/{collectionName}/search": { + "post": { + "tags": ["document"], + "summary": "Searches Documents in a DocumentCollection", + "description": "Searches Documents in a DocumentCollection based on provided search criteria.", + "parameters": [ + { + "name": "collectionName", + "in": "path", + "description": "Name of the Document Collection", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "limit", + "in": "query", + "description": "Limit the number of returned documents", + "schema": { "type": "integer" } + } + ], + "requestBody": { + "description": "Search criteria", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.DocumentSearchPayload" } } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.DocumentSearchResultPage" } } + } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "401": { + "description": "Unauthorized", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "searchPayload" + } + }, + "/sessions": { + "get": { + "tags": ["session"], + "summary": "Returns all sessions", + "description": "Get all sessions with optional limit and cursor for pagination.", + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "Limit the number of results returned", + "schema": { "type": "integer" } + }, + { "name": "cursor", "in": "query", "description": "Cursor for pagination", "schema": { "type": "integer" } } + ], + "responses": { + "200": { + "description": "List of sessions", + "content": { + "application/json": { + "schema": { "type": "array", "items": { "$ref": "#/components/schemas/models.Session" } } + } + } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + }, + "post": { + "tags": ["session"], + "summary": "Add a session", + "description": "add session by id", + "requestBody": { + "description": "Session", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.CreateSessionRequest" } } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.Session" } } } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "session" + } + }, + "/sessions/{sessionId}": { + "get": { + "tags": ["session"], + "summary": "Returns a session by ID", + "description": "get session by id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.Session" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + }, + "patch": { + "tags": ["session"], + "summary": "Add a session", + "description": "add session by id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Session", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.UpdateSessionRequest" } } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.Session" } } } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "session" + } + }, + "/sessions/{sessionId}/classify": { + "post": { + "tags": ["session"], + "summary": "Classify a session", + "description": "classify a session by session id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Classify request", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.ClassifySessionRequest" } } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.ClassifySessionResponse" } } + } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "classifyRequest" + } + }, + "/sessions/{sessionId}/memory": { + "get": { + "tags": ["memory"], + "summary": "Returns a memory (latest summary and list of messages) for a given session", + "description": "get memory by session id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "memoryType", + "in": "query", + "description": "memoryType: perpetual or message_window", + "schema": { "type": "string" } + }, + { + "name": "lastn", + "in": "query", + "description": "Last N messages. Overrides memory_window configuration", + "schema": { "type": "integer" } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "type": "array", "items": { "$ref": "#/components/schemas/models.Memory" } } + } + } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + }, + "post": { + "tags": ["memory"], + "summary": "Add memory messages to a given session", + "description": "add memory messages by session id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Memory messages", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.Memory" } } }, + "required": true + }, + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "memoryMessages" + }, + "delete": { + "tags": ["memory"], + "summary": "Delete memory messages for a given session", + "description": "delete memory messages by session id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + }, + "/sessions/{sessionId}/messages": { + "get": { + "tags": ["messages"], + "summary": "Retrieves all messages for a specific session", + "description": "get messages by session id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "type": "array", "items": { "$ref": "#/components/schemas/models.Message" } } + } + } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + } + } + }, + "/sessions/{sessionId}/messages/{messageId}": { + "get": { + "tags": ["messages"], + "summary": "Retrieves a specific message", + "description": "get message by session id and message id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "messageId", + "in": "path", + "description": "Message ID", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.Message" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + } + }, + "patch": { + "tags": ["messages"], + "summary": "Updates the metadata of a specific message", + "description": "update message metadata by session id and message id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "messageId", + "in": "path", + "description": "Message ID", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "New Metadata", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.Message" } } }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.Message" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/sessions/{sessionId}/search": { + "post": { + "tags": ["search"], + "summary": "Search memory messages for a given session", + "description": "search memory messages by session id and query", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + }, + { + "name": "limit", + "in": "query", + "description": "Limit the number of results returned", + "schema": { "type": "integer" } + } + ], + "requestBody": { + "description": "Search query", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.MemorySearchPayload" } } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "type": "array", "items": { "$ref": "#/components/schemas/models.MemorySearchResult" } } + } + } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "searchPayload" + } + }, + "/sessions/{sessionId}/summaries": { + "get": { + "tags": ["session"], + "summary": "Returns a session's summaries by ID", + "description": "Get session summaries by ID", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/models.SummaryListResponse" } } + } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + }, + "/sessions/{sessionId}/synthesize_question": { + "get": { + "tags": ["memory"], + "summary": "Synthesize a question for a given session", + "description": "synthesize a question by session id", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "description": "Session ID", + "required": true, + "schema": { "type": "string" } + }, + { "name": "lastNMessages", "in": "query", "description": "Last N messages", "schema": { "type": "integer" } } + ], + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.Question" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + }, + "/user": { + "get": { + "tags": ["user"], + "summary": "List all users", + "description": "List all users with pagination.", + "parameters": [ + { + "name": "pageNumber", + "in": "query", + "description": "Page number for pagination, starting from 1", + "schema": { "type": "integer" } + }, + { + "name": "pageSize", + "in": "query", + "description": "Number of users to retrieve per page", + "schema": { "type": "integer" } + } + ], + "responses": { + "200": { + "description": "Successfully retrieved list of users", + "content": { + "application/json": { + "schema": { "type": "array", "items": { "$ref": "#/components/schemas/models.User" } } + } + } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + }, + "post": { + "tags": ["user"], + "summary": "Add a user", + "description": "add user by id", + "requestBody": { + "description": "User", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.CreateUserRequest" } } }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.User" } } } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "user" + } + }, + "/users-ordered": { + "get": { + "tags": ["user"], + "summary": "List all users", + "description": "list all users", + "parameters": [ + { "name": "limit", "in": "query", "description": "Limit", "schema": { "type": "integer" } }, + { "name": "cursor", "in": "query", "description": "Cursor", "schema": { "type": "integer" } } + ], + "responses": { + "200": { + "description": "Successfully retrieved list of users", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "type": "array", "items": { "$ref": "#/components/schemas/models.User" } } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + }, + "/users/{userId}": { + "get": { + "tags": ["user"], + "summary": "Returns a user by ID", + "description": "get user by id", + "parameters": [ + { "name": "userId", "in": "path", "description": "User ID", "required": true, "schema": { "type": "string" } } + ], + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.User" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + }, + "delete": { + "tags": ["user"], + "summary": "Delete a user", + "description": "delete user by id", + "parameters": [ + { "name": "userId", "in": "path", "description": "User ID", "required": true, "schema": { "type": "string" } } + ], + "responses": { + "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "string" } } } }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + }, + "patch": { + "tags": ["user"], + "summary": "Update a user", + "description": "update user by id", + "parameters": [ + { "name": "userId", "in": "path", "description": "User ID", "required": true, "schema": { "type": "string" } } + ], + "requestBody": { + "description": "User", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.UpdateUserRequest" } } }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/models.User" } } } + }, + "400": { + "description": "Bad Request", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "404": { + "description": "Not Found", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }], + "x-codegen-request-body-name": "user" + } + }, + "/users/{userId}/sessions": { + "get": { + "tags": ["user"], + "summary": "List all sessions for a user", + "description": "list all sessions for a user by user id", + "parameters": [ + { "name": "userId", "in": "path", "description": "User ID", "required": true, "schema": { "type": "string" } } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { "type": "array", "items": { "$ref": "#/components/schemas/models.Session" } } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/apihandlers.APIError" } } } + } + }, + "security": [{ "Bearer": [] }] + } + } + }, + "components": { + "schemas": { + "apihandlers.APIError": { "type": "object", "properties": { "message": { "type": "string" } } }, + "models.ClassifySessionRequest": { + "type": "object", + "properties": { + "classes": { "type": "array", "items": { "type": "string" } }, + "last_n": { "type": "integer" }, + "name": { "type": "string" }, + "persist": { "type": "boolean" }, + "session_id": { "type": "string" } + } + }, + "models.ClassifySessionResponse": { + "type": "object", + "properties": { "class": { "type": "string" }, "name": { "type": "string" } } + }, + "models.CreateDocumentCollectionRequest": { + "required": ["embedding_dimensions", "is_auto_embedded", "name"], + "type": "object", + "properties": { + "description": { "maxLength": 1000, "type": "string" }, + "embedding_dimensions": { "maximum": 2000, "minimum": 8, "type": "integer" }, + "is_auto_embedded": { + "type": "boolean", + "description": "these needs to be pointers so that we can distinguish between false and unset when validating" + }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "name": { "maxLength": 40, "minLength": 3, "type": "string" } + } + }, + "models.CreateDocumentRequest": { + "type": "object", + "properties": { + "content": { "type": "string" }, + "document_id": { "maxLength": 100, "type": "string" }, + "embedding": { "type": "array", "items": { "type": "number" } }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } } + } + }, + "models.CreateSessionRequest": { + "type": "object", + "properties": { + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "session_id": { "type": "string" }, + "user_id": { "type": "string", "description": "Must be a pointer to allow for null values" } + } + }, + "models.CreateUserRequest": { + "type": "object", + "properties": { + "email": { "type": "string" }, + "first_name": { "type": "string" }, + "last_name": { "type": "string" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "user_id": { "type": "string" } + } + }, + "models.DocumentCollectionResponse": { + "type": "object", + "properties": { + "created_at": { "type": "string" }, + "description": { "type": "string" }, + "document_count": { "type": "integer", "description": "Number of documents in the collection" }, + "document_embedded_count": { "type": "integer", "description": "Number of documents with embeddings" }, + "embedding_dimensions": { "type": "integer" }, + "embedding_model_name": { "type": "string" }, + "is_auto_embedded": { "type": "boolean" }, + "is_indexed": { "type": "boolean" }, + "is_normalized": { "type": "boolean" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "name": { "type": "string" }, + "updated_at": { "type": "string" }, + "uuid": { "type": "string" } + } + }, + "models.DocumentResponse": { + "type": "object", + "properties": { + "content": { "type": "string" }, + "created_at": { "type": "string" }, + "document_id": { "type": "string" }, + "embedding": { "type": "array", "items": { "type": "number" } }, + "is_embedded": { "type": "boolean" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "updated_at": { "type": "string" }, + "uuid": { "type": "string" } + } + }, + "models.DocumentSearchPayload": { + "type": "object", + "properties": { + "collection_name": { "type": "string" }, + "embedding": { "type": "array", "items": { "type": "number" } }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "min_score": { "type": "number", "description": "TODO: implement for documents" }, + "mmr_lambda": { "type": "number" }, + "search_type": { "$ref": "#/components/schemas/models.SearchType" }, + "text": { "type": "string" } + } + }, + "models.DocumentSearchResult": { + "type": "object", + "properties": { + "content": { "type": "string" }, + "created_at": { "type": "string" }, + "document_id": { "type": "string" }, + "embedding": { "type": "array", "items": { "type": "number" } }, + "is_embedded": { "type": "boolean" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "score": { "type": "number" }, + "updated_at": { "type": "string" }, + "uuid": { "type": "string" } + } + }, + "models.DocumentSearchResultPage": { + "type": "object", + "properties": { + "current_page": { "type": "integer" }, + "query_vector": { "type": "array", "items": { "type": "number" } }, + "result_count": { "type": "integer" }, + "results": { "type": "array", "items": { "$ref": "#/components/schemas/models.DocumentSearchResult" } }, + "total_pages": { "type": "integer" } + } + }, + "models.GetDocumentListRequest": { + "type": "object", + "properties": { + "document_ids": { "type": "array", "items": { "type": "string" } }, + "uuids": { "type": "array", "items": { "type": "string" } } + } + }, + "models.Memory": { + "type": "object", + "properties": { + "messages": { "type": "array", "items": { "$ref": "#/components/schemas/models.Message" } }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "summary": { "$ref": "#/components/schemas/models.Summary" } + } + }, + "models.MemorySearchPayload": { + "type": "object", + "properties": { + "embedding": { "type": "array", "items": { "type": "number" } }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "min_score": { "type": "number" }, + "mmr_lambda": { "type": "number" }, + "search_scope": { "$ref": "#/components/schemas/models.SearchScope" }, + "search_type": { "$ref": "#/components/schemas/models.SearchType" }, + "text": { "type": "string" } + } + }, + "models.MemorySearchResult": { + "type": "object", + "properties": { + "embedding": { "type": "array", "items": { "type": "number" } }, + "message": { "$ref": "#/components/schemas/models.Message" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "score": { "type": "number" }, + "summary": { "$ref": "#/components/schemas/models.Summary" } + } + }, + "models.Message": { + "type": "object", + "properties": { + "content": { "type": "string" }, + "created_at": { "type": "string" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "role": { "type": "string" }, + "role_type": { "type": "string" }, + "token_count": { "type": "integer" }, + "updated_at": { "type": "string" }, + "uuid": { "type": "string" } + } + }, + "models.Question": { "type": "object", "properties": { "question": { "type": "string" } } }, + "models.SearchScope": { + "type": "string", + "enum": ["messages", "summary"], + "x-enum-varnames": ["SearchScopeMessages", "SearchScopeSummary"] + }, + "models.SearchType": { + "type": "string", + "enum": ["similarity", "mmr"], + "x-enum-varnames": ["SearchTypeSimilarity", "SearchTypeMMR"] + }, + "models.Session": { + "type": "object", + "properties": { + "created_at": { "type": "string" }, + "deleted_at": { "type": "string" }, + "id": { "type": "integer" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "project_uuid": { "type": "string" }, + "session_id": { "type": "string" }, + "updated_at": { "type": "string" }, + "user_id": { "type": "string", "description": "Must be a pointer to allow for null values" }, + "uuid": { "type": "string" } + } + }, + "models.Summary": { + "type": "object", + "properties": { + "content": { "type": "string" }, + "created_at": { "type": "string" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "related_message_uuids": { "type": "array", "items": { "type": "string" } }, + "token_count": { "type": "integer" }, + "uuid": { "type": "string" } + } + }, + "models.SummaryListResponse": { + "type": "object", + "properties": { + "row_count": { "type": "integer" }, + "summaries": { "type": "array", "items": { "$ref": "#/components/schemas/models.Summary" } }, + "total_count": { "type": "integer" } + } + }, + "models.UpdateDocumentCollectionRequest": { + "type": "object", + "properties": { + "description": { "maxLength": 1000, "type": "string" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } } + } + }, + "models.UpdateDocumentListRequest": { + "required": ["uuid"], + "type": "object", + "properties": { + "document_id": { "maxLength": 40, "type": "string" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "uuid": { "type": "string" } + } + }, + "models.UpdateDocumentRequest": { + "type": "object", + "properties": { + "document_id": { "maxLength": 40, "type": "string" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } } + } + }, + "models.UpdateSessionRequest": { + "type": "object", + "properties": { + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "session_id": { "type": "string" } + } + }, + "models.UpdateUserRequest": { + "type": "object", + "properties": { + "email": { "type": "string" }, + "first_name": { "type": "string" }, + "last_name": { "type": "string" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "user_id": { "type": "string" }, + "uuid": { "type": "string" } + } + }, + "models.User": { + "type": "object", + "properties": { + "created_at": { "type": "string" }, + "deleted_at": { "type": "string" }, + "email": { "type": "string" }, + "first_name": { "type": "string" }, + "id": { "type": "integer" }, + "last_name": { "type": "string" }, + "metadata": { "type": "object", "additionalProperties": { "type": "object" } }, + "project_uuid": { "type": "string" }, + "session_count": { "type": "integer" }, + "updated_at": { "type": "string" }, + "user_id": { "type": "string" }, + "uuid": { "type": "string" } + } + } + }, + "securitySchemes": { + "Bearer": { + "type": "apiKey", + "description": "Type \"Api-Key\" followed by a space and JWT token.", + "name": "Authorization", + "in": "header" + } + } + }, + "x-original-swagger-version": "2.0" +} diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/creates-a-new-project.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/creates-a-new-project.mdx new file mode 100644 index 00000000000..c3f61986df7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/creates-a-new-project.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /projects +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/deletes-an-api-key-for-a-project.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/deletes-an-api-key-for-a-project.mdx new file mode 100644 index 00000000000..80e1f207595 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/deletes-an-api-key-for-a-project.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /accounts/{accountId}/api-keys/{apiKeyId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/deletes-an-existing-project.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/deletes-an-existing-project.mdx new file mode 100644 index 00000000000..ba7ad97afae --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/deletes-an-existing-project.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /projects/{projectId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/generates-a-new-api-key-for-a-project.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/generates-a-new-api-key-for-a-project.mdx new file mode 100644 index 00000000000..1a7b0e97c18 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/generates-a-new-api-key-for-a-project.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /accounts/{accountId}/api-keys +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/retrieves-a-project.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/retrieves-a-project.mdx new file mode 100644 index 00000000000..e521cf49f04 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/retrieves-a-project.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /projects/{projectId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/retrieves-api-keys-for-a-project.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/retrieves-api-keys-for-a-project.mdx new file mode 100644 index 00000000000..fe74b81afed --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/retrieves-api-keys-for-a-project.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /accounts/{accountId}/api-keys +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/updates-an-existing-project.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/updates-an-existing-project.mdx new file mode 100644 index 00000000000..a0655e665c3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/project/updates-an-existing-project.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /projects/{projectId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/search/search-memory-messages-for-a-given-session.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/search/search-memory-messages-for-a-given-session.mdx new file mode 100644 index 00000000000..a857b9f7e1d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/search/search-memory-messages-for-a-given-session.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /sessions/{sessionId}/search +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/add-a-session-1.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/add-a-session-1.mdx new file mode 100644 index 00000000000..c5041d7bf1c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/add-a-session-1.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /sessions/{sessionId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/add-a-session.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/add-a-session.mdx new file mode 100644 index 00000000000..154d00bdc2d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/add-a-session.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /sessions +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/classify-a-session.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/classify-a-session.mdx new file mode 100644 index 00000000000..3ed36647270 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/classify-a-session.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /sessions/{sessionId}/classify +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-a-session-by-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-a-session-by-id.mdx new file mode 100644 index 00000000000..ab8aad011ac --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-a-session-by-id.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /sessions/{sessionId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-a-sessions-summaries-by-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-a-sessions-summaries-by-id.mdx new file mode 100644 index 00000000000..ab12cceb436 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-a-sessions-summaries-by-id.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /sessions/{sessionId}/summaries +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-all-sessions.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-all-sessions.mdx new file mode 100644 index 00000000000..7ad2585027f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/session/returns-all-sessions.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /sessions +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/add-a-user.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/add-a-user.mdx new file mode 100644 index 00000000000..70cea274571 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/add-a-user.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /user +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/delete-a-user.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/delete-a-user.mdx new file mode 100644 index 00000000000..417d2fb68d8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/delete-a-user.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /users/{userId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-sessions-for-a-user.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-sessions-for-a-user.mdx new file mode 100644 index 00000000000..af9071dcb50 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-sessions-for-a-user.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /users/{userId}/sessions +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-users-1.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-users-1.mdx new file mode 100644 index 00000000000..44aa0e1c547 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-users-1.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /users-ordered +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-users.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-users.mdx new file mode 100644 index 00000000000..e363526fbdb --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/list-all-users.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /user +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/returns-a-user-by-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/returns-a-user-by-id.mdx new file mode 100644 index 00000000000..dd41db87ebd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/returns-a-user-by-id.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /users/{userId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/update-a-user.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/update-a-user.mdx new file mode 100644 index 00000000000..8a9853578d8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/api-reference/user/update-a-user.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /users/{userId} +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/memories.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/memories.mdx new file mode 100644 index 00000000000..6e400641966 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/memories.mdx @@ -0,0 +1,193 @@ +--- +title: "Memories" +description: "Memories are the central data structure in Zep's Memory Store. They contain a list of Messages and a Summary." +--- + + + A Memory is the central data structure in Zep's Memory Store. It contains a list of Messages and a Summary (if + created). + + The Memory and Summary are returned with UUIDs, token counts, timestamps, and other metadata. + Memories are associated with [Sessions](sessions) in a many-to-one relationship. + + +## Persisting a Memory to a Session + +A Memory may include a single message or a series of messages. Each Message has a `role`, `role_type` and `content` field, with +role being the identifiers for your human and AI/agent users, and content being the text of the message. + +Additionally, you can store custom metadata with each Message. + + +**Sessions don't need to be explicitly created** + +Sessions are created automatically when adding Memories. If the `SessionID` is already exists, then the Memory is +upserted into the Session. + +[Manually creating a session](sessions) can be useful if you want to associate it with a user or add metadata. + + + + + + +```python +from zep_python import ( + NotFoundError, + ZepClient, +) +from zep_python.memory import Memory +from zep_python.message import Message +import uuid + +API_KEY = "" +client = ZepClient(api_key=API_KEY) + +session_id = uuid.uuid4().hex # A new session identifier + +history = [ + {"role": "Jane", "role_type": "user", "content": "Who was Octavia Butler?"}, + {"role": "HistoryBot", "role_type": "assistant", "content": "Octavia Estelle Butler (June 22, 1947 – February 24, 2006) was an American science fiction author."}, + {"role": "Jane", "role_type": "user", "content": "Which books of hers were made into movies?", "metadata": {"foo": "bar"}}, +] + +messages = [Message(role=m["role"], content=m["content"], metadata=m.get("metadata")) for m in history] +memory = Memory(messages=messages) +result = client.memory.add_memory(session_id, memory) +``` + + + + +```typescript +import { ZepClient, Message, Memory } from "@getzep/zep-js"; +import * as uuid from "uuid"; + +const API_KEY = ""; +const zepClient = await ZepClient.init(process.env.ZEP_API_KEY); + +const session_id: string = uuid.v4(); // A new session identifier + +const history: { role: string; content: string; metadata?: { [key: string]: any } }[] = [ + { role: "Jane", role_type: "user", content: "Who was Octavia Butler?" }, + { + role: "HistoryBot", + role_type: "assistant", + content: "Octavia Estelle Butler (June 22, 1947 – February 24, 2006) was an American science fiction author.", + }, + { role: "Jane", role_type: "user", content: "Which books of hers were made into movies?", metadata: { foo: "bar" } }, +]; + +const messages: Message[] = history.map((m) => new Message({ role: m.role, content: m.content, metadata: m.metadata })); +const memory: Memory = new Memory({ messages }); +const result = client.memory.addMemory(session_id, memory); +``` + + + + +## Getting a Session's Memory + +Read more about the difference between [Perpetual and Message Window Buffer Memory](/chat-history-memory). + +### Perpetual Memory + +The example below uses Zep's async API and a context manager. You may also use the sync API's `client.memory.get_memory` +method and use the `client` outside of a context manager. + + + + +```python +async with ZepClient(api_key=api_key) as client: + try: + memory = await client.memory.aget_memory(session_id, memory_type="perpetual") + for message in memory.messages: + print(message.to_dict()) + except NotFoundError: + print("Memory not found") +``` + + + + +```typescript +const client = await ZepClient.init(api_key); +try { + const memory = await client.memory.getMemory(session_id, "perpetual"); + for (const message of memory.messages) { + console.log(message.toDict()); + } +} catch (error) { + if (error instanceof NotFoundError) { + console.log("Memory not found"); + } else { + throw error; + } +} +``` + + + + +```json title="Output:" +[ + { + "uuid": "7291333f-2e01-4b06-9fe0-3efc59b3399c", + "created_at": "2023-05-16T21:59:11.057919Z", + "role": "HistoryBot", + "role_type": "assistant", + "content": "Parable of the Sower is a science fiction novel by Octavia Butler, published in 1993. It follows the story of Lauren Olamina, a young woman living in a dystopian future where society has collapsed due to environmental disasters, poverty, and violence.", + "token_count": 56 + }, + { + "uuid": "61f862c5-945b-49b1-b87c-f9338518b7cb", + "created_at": "2023-05-16T21:59:11.057919Z", + "role": "Jane", + "role_type": "user", + "content": "Write a short synopsis of Butler's book, Parable of the Sower. What is it about?", + "token_count": 23 + } +] +``` + +### Message Window Buffer Memory + + + + + +```python +async with ZepClient(api_key) as client: + try: + memory = await client.memory.aget_memory(session_id) + for message in memory.messages: + print(message.to_dict()) + except NotFoundError: + print("Memory not found") +``` + + + + +```typescript +const client = await ZepClient.init(api_key); +try { + const memory = await client.memory.getMemory(session_id, "message_window"); + for (const message of memory.messages) { + console.log(message.toDict()); + } +} catch (error) { + if (error instanceof NotFoundError) { + console.log("Memory not found"); + } else { + throw error; + } +} +``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/messages.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/messages.mdx new file mode 100644 index 00000000000..0d8abc12da9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/messages.mdx @@ -0,0 +1,194 @@ + + Messages are the individual components of a conversation, each linked to a [Session](sessions) in a many-to-one + relationship. To incorporate messages into sessions, utilize the [Memories](memories) feature. + + +## Get a Specific Message from a Session + +To fetch a particular message from a session, utilize the `get_session_message` method. Below are examples in Python and TypeScript: + + + + + +```python +async with ZepClient(api_key=api_key) as client: + try: + session_id = "3e0e4af9-71ff-4541-b206-6133574bbbc6" # Replace with the actual session_id + message_id = "3e0e4af9-71ff-4541-b206-6133574bbbc7" # Replace with the actual message_id + message = await client.message.aget_session_message(session_id, message_id) + print(message.to_dict()) + except NotFoundError: + print("Message not found") +``` + + + + +```typescript +const client: ZepClient = await ZepClient.init(api_key); + +try { + const session_id: string = "3e0e4af9-71ff-4541-b206-6133574bbbc6"; // Replace with the actual session_id + const message_id: string = "3e0e4af9-71ff-4541-b206-6133574bbbc7"; // Replace with the actual message_id + + const message: Message = await client.message.getSessionMessage(session_id, message_id); + console.log(message); +} catch (error) { + if (error instanceof NotFoundError) { + console.log("Message not found"); + } else { + throw error; + } +} +``` + + + + +```json +{ + "uuid": "3e0e4af9-71ff-4541-b206-6133574bbbc7", + "created_at": "2023-12-08T22:17:33.185756Z", + "updated_at": "0001-01-01T00:00:00Z", + "role": "human", + "role_type": "user", + "content": "Who were her contemporaries?", + "metadata": { + "system": { + "entities": [], + "intent": "The subject is requesting information about the people who were living at the same time as the woman in question." + } + } +} +``` + +## Getting all Messages from a Session + + + + + +```python +async with ZepClient(api_key) as client: + try: + messages = await client.message.aget_session_messages(session_id) + for message in messages: + print(message.to_dict()) + except NotFoundError: + print("Sesssion not found") +``` + + + + +```typescript +const client: ZepClient = await ZepClient.init(api_key); + +try { + const messages: Message[] = await client.message.getSessionMessages(session_id); + messages.forEach((message: Message) => { + console.log(message); + }); +} catch (error) { + if (error instanceof NotFoundError) { + console.log("Message not found"); + } else { + throw error; + } +} +``` + + + + +```json +{ + "messages": [ + { + "uuid": "3e0e4af9-71ff-4541-b206-6133574bbbc7", + "created_at": "2023-12-08T22:17:33.185756Z", + "updated_at": "0001-01-01T00:00:00Z", + "role": "human", + "role_type": "user", + "content": "Who were her contemporaries?", + "metadata": { + "system": { + "entities": [], + "intent": "The subject is requesting information about the people who were living at the same time as the woman in question." + } + }, + "token_count": 0 + } + ], + ... +} +``` + +## Updating Session Message Metadata + +You have the ability to modify the metadata of a message. Please provide the metadata in the following format. Note that at this time, it's not possible to update the content of a message itself. + +```json +{ + "metadata": { + "foo": "bar" + } +} +``` + + + + + +```python +async with ZepClient(api_key) as client: + try: + session_id = "3e0e4af9-71ff-4541-b206-6133574bbbc6" # Replace with the actual session_id + message_uuid = "3e0e4af9-71ff-4541-b206-6133574bbbc7" # Replace with the actual message_id + metadata = { + "metadata": { + "foo": "bar" + } + } + await client.message.update_session_message_metadata(session_id, message_id, metadata) + except NotFoundError: + print("Session not found") +``` + + + + +```typescript +const client: ZepClient = await ZepClient.init(api_key); + +try { + const session_id: string = "3e0e4af9-71ff-4541-b206-6133574bbbc6"; // Replace with the actual session_id + const message_uuid: string = "3e0e4af9-71ff-4541-b206-6133574bbbc7"; // Replace with the actual message_id + const metadata: any = { metadata: { foo: "bar" } }; // Replace with the desired metadata + await client.message.updateSessionMessageMetadata(session_id, message_uuid, metadata); +} catch (error) { + console.error("Session not found", error); +} +``` + + + + +```json +{ + "uuid": "3e0e4af9-71ff-4541-b206-6133574bbbc7", + "created_at": "2023-12-08T22:17:33.185756Z", + "updated_at": "0001-01-01T00:00:00Z", + "role": "human", + "role_type": "user", + "content": "Who were her contemporaries?", + "metadata": { + "foo": "bar", + "system": { + "entities": [], + "intent": "The subject is requesting information about the people who were living at the same time as the woman in question." + } + } +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/overview.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/overview.mdx new file mode 100644 index 00000000000..24ff99cb575 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/overview.mdx @@ -0,0 +1,75 @@ +--- +title: "Overview" +description: "With Zep's Chat History features you can ensure that your your assistant's responses are always relevant to the +conversation at hand." +--- + +Zep's Memory API persists your users' chat history and metadata to a [Session](sessions), enriches the memory, and +enables vector similarity search over historical chat messages and dialog summaries. + +Zep offers several approaches to populating prompts with context from historical conversations. + +## Perpetual Memory + +Perpetual Memory is the default memory type. Salient facts from the dialog are extracted and stored in a `Fact Table`. This +is updated in real-time as new messages are added to the Session. + +Every time you call the Memory API to get a Memory, +Zep returns the Fact Table, the most recent messages (per your `Message Window` setting), and a summary of the most recent +messages prior to the `Message Window`. + +We've found that including the combination of the Fact Table, summary, and the most recent messages in a prompts provides both factual +context and nuance to the LLM. + + + Perpetual Memory + + +## Summary Retriever Memory + +The Memory API returns the most recent messages and a summary of past messages relevant to the +current conversation, enabling you to provide your Assistant with helpful context from past conversations. In essence, +Perpetual memory +is a RAG pipeline over the entire conversation history and that executes in hundreds +of milliseconds. + +You don't need to do anything to enable this feature, beyond adding messages to a Session and +calling the `get_memory` API and requesting the `perpetual` `memory_type`. + + + Want to learn more about how Summary Retriever works? [Read How Perpetual Memory + Works](#how-summary-retriever-memory-works). + + +## Message Window Buffer Memory + +The Memory API returns the most recent `N` messages from the current conversation, where `N` is a +configurable `Message Window` set in your [Project Settings](/getting-started/projects). + +A summary of a messages older than the `N`th most recent message is also returned, if available. + +## How Summary Retriever Memory Works + +Perpetual Memory extracts the most relevant historical dialog from a Session and, with very low latency, returns to via +the Zep Memory API. + +When new Messages are added to a Session, Zep generates a new summary from a trailing series of messages in the dialog. +This is ongoing, ensuring that the entire conversation is incrementally summarized. +These summaries are also embedded, enabling vector similarity search over the entire series of conversation summaries. + + + Summary Retriever Flow Chart + + +Every time you call the Memory API to get a Memory, Zep uses a proprietary, low-latency language model to generate a +standalone +question from the most recent messages in the Session. This question is then used to search over the entire series of +conversation summaries. + +The most relevant summaries are returned and re-ranked in order to +improve their relevance and diversity. The top summaries are then themselves summarized using a +low-latency summarization model. The resulting summary and most recent messages are then returned via the API for use in +populating your prompt. + +The entire process takes hundreds of milliseconds, so that you're able to rapidly populate your prompt with relevant +context. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/search.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/search.mdx new file mode 100644 index 00000000000..f5c184730f4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/search.mdx @@ -0,0 +1,190 @@ +--- +title: "Vector Search Over Chat History" +description: "Zep helps developers search through long-term memory stores to find relevant historical conversations efficiently. With automated embedding and advanced indexing, Zep offers robust search capabilities that are straightforward and effective." +--- + +## Searching for Messages or Summaries + +Zep enables vector similarity searches for Messages or Summaries stored within its system. This feature lets you populate prompts with past conversations that are contextually similar to a specific query, organizing the results by a similarity Score. + +### Choosing Between Summaries and Messages + +Zep supports searches for both Messages and Summaries. Since individual messages might miss some conversational context, Summaries are often the preferred choice for executing searches. For more on this, check out the section on [message limitations](#limitations-when-searching-over-messages-or-short-document-chunks). + +### MMR Reranking for Summaries + +Summaries can sometimes overlap in information, especially when the Message Window is set low. In such cases, employing [Maximum Marginal Relevance (MMR)](/working-with-search#how-zeps-mmr-re-ranking-works) to rerank search results can be beneficial. Zep includes built-in, hardware-accelerated support for MMR, making it simple and easy to use. + + +**Constructing Search Queries** + +Zep's Collection and Memory search support semantic search queries, JSONPath-based metadata filters, and a combination +of both. + +Memory search also supports querying by message creation date. + +Read more about [constructing search queries](/working-with-search#maximal-marginal-relevance-re-ranking). + + + + + + + +```python +from zep_python import ( + MemorySearchPayload, + ZepClient, +) + +# This uniquely identifies the user's session +session_id = "my_session_id" + +# Initialize the Zep client before running this code +search_payload = MemorySearchPayload( + text="Is Lauren Olamina a character in a book?", + search_scope="summary", # This could be messages or summary + search_type="mmr", # remove this if you'd prefer not to rerank results + mmr_lambda=0.5, # tune diversity vs relevance +) + +search_results = await client.memory.asearch_memory(session_id, search_payload) + +for search_result in search_results: + # Uncomment for message search + # print(search_result.messsage.dict()) + print(search_result.summary.dict()) +``` + + + + +```typescript +import { MemorySearchPayload, ZepClient } from "@getzep/zep-js"; + +// This uniquely identifies the user's session +const session_id: string = "my_session_id"; + +// Initialize the Zep client before running this code +const search_payload: MemorySearchPayload = { + text: "Is Lauren Olamina a character in a book?", + search_scope: "summary", // This could be messages or summary + search_type: "mmr", // Remove this if you'd prefer not to rerank results + mmr_lambda: 0.5, // Tune diversity vs relevance +}; + +const search_results = await client.memory.searchMemory(session_id, search_payload); + +search_results.forEach((search_result) => { + // Uncomment for message search + // console.log(search_result.message); + console.log(search_result.summary); +}); +``` + + + + +```json +{ + "summary": { + "uuid": "b47b83da-16ae-49c8-bacb-f7d049f9df99", + "created_at": "2023-11-02T18:22:10.103867Z", + "content": "The human asks the AI to explain the book Parable of the Sower by Octavia Butler. The AI responds by explaining that Parable of the Sower is a science fiction novel by Octavia Butler. The book follows the story of Lauren Olamina, a young woman living in a dystopian future where society has collapsed due to environmental disasters, poverty, and violence.", + "token_count": 66 + }, + "metadata": null, + "dist": 0.8440576791763306 +} +``` + +## Hybrid Search for Chat History with Metadata Filters + +Besides the vector similarity search for Messages and Summaries stored in Zep, you can also use metadata filters for your searches. You also have the option to conduct searches based purely on metadata. + + + + + +```python +zep_client.memory.search_memory( + session_id=session_id, + search_payload=MemorySearchPayload( + query="I enjoy reading science fiction.", + metadata={ + "where": {"jsonpath": '$[*] ? (@.foo == "bar")'}, + }, + ), +) +``` + + + + +```typescript +await client.memory.searchMemory(session_id, { + query: "I enjoy reading science fiction.", + metadata: { + where: { jsonpath: '$[*] ? (@.foo == "bar")' }, + }, +}); +``` + + + + +```json +{ + "dist": 0.7170433826192629, + "message": { + "content": "I've read many books written by Octavia Butler.", + "created_at": "2023-06-03T22:00:43.034056Z", + "metadata": { + "foo": "bar", + "system": { + "entities": [ + { + "Label": "PERSON", + "Matches": [ + { + "End": 46, + "Start": 32, + "Text": "Octavia Butler" + } + ], + "Name": "Octavia Butler" + } + ] + } + }, + "role": "human", + "token_count": 13, + "uuid": "8f3a06dd-0625-41da-a2af-b549f2056b3f" + }, + "metadata": null, + "summary": null +} +``` + +### Search Ranking and Limits + +#### Vector Indexes + +Zep automatically creates HNSW (Hierarchical Navigable Small World) indexes for all messages, summaries, and documents. This means you get speedy and relevant search results right out of the box, without the hassle of manually setting up or integrating a vector store and building indexes. Zep uses an optimized distance function similar to cosine distance for search ranking. + +#### Embedding Models + +Zep uses the `BAAI/bge-large-en` model for text embeddings, known for its high performance and optimization for semantic search. Keep in mind, this model has a 512 token maximum sequence length, which is important when deciding how to chunk your documents. + +#### Limitations When Searching Over Messages or Short Document Chunks + +Zep can return all messages from a search up to a certain row limit. This limit can be adjusted by passing a `limit` query string argument to the search API. Due to the sparsity issue we'll touch on below, we recommend sticking to the top 2-3 messages in your prompts. Or, you could analyze your search results and use a distance threshold to filter out messages that aren't relevant. + + +**Handling Short Texts in Embeddings** + +Searching through chat histories can be tricky. Chat messages are often brief and might not carry much "information". When these short texts are turned into high-dimensional embedding vectors, the result can be very sparse vectors. + +This sparsity means a lot of these vectors end up being close to each other in the vector space, which can lead to a higher chance of getting false positives in your search results for relevant messages. As a result, we recommend searching over Summaries, which include more information than Messages. + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/sessions.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/sessions.mdx new file mode 100644 index 00000000000..63b247d9686 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/sessions.mdx @@ -0,0 +1,176 @@ + +Sessions represent a conversation. Sessions can be associated with [Users](/users) in a 1:M relationship. + +Chat messages are added to sessions in the form of [Memories](memories). Each session can have many messages +associated with it. + +The `SessionID` is a string key that accepts arbitrary identifiers. Related data you'd like to store can be persisted +as metadata. + + + +## Adding a Session + +`SessionIDs` are arbitrary identifiers that you can map to relevant business objects in your app, such as users or a +conversation a user might have with your app + + +**Sessions don't need to be explicitly created** + +Sessions are created automatically when adding Memories. If the SessionID already exists, then the Memory is upserted into the Session. + +Manually creating a session can be useful if you want to associate it with a user or add metadata. + + + + + + +```python +async with ZepClient(api_key=api_key) as client: + session_id = uuid.uuid4().hex # A new session identifier + session = Session( + session_id=session_id, + user_id=user_id, # Optionally associate this session with a user + metadata={"foo" : "bar"} + ) + await client.memory.aadd_session(session) +``` + + + + +```typescript +const client: ZepClient = await ZepClient.init(api_key); +const session_id: string = uuid.v4(); // Generate a new session identifier +const session: Session = new Session({ + session_id, + user_id: user.user_id, // Optionally associate this session with a user + metadata: { foo: "bar" }, +}); +await client.memory.addSession(session); +``` + + + + +Looking to associate a Session with a User? Check out our [User Management](/users) docs. + +## Updating Session Metadata + +You can update a session's metadata by providing a Session object with new metadata. Note that +metadata is merged, so any existing metadata will be preserved. + + + + + +```python +session = Session(session_id=session_id, metadata={"qax" : "baz"}) +await client.memory.aupdate_session(session) +``` + + + + +```typescript +const session: Session = new Session({ + session_id, + metadata: { qax: "baz" }, +}); +await client.memory.updateSession(session); +``` + + + + +## Getting a Session + + + + + +```python +session = await client.memory.aget_session(session_id) +print(session.dict()) +``` + + + + +```typescript +const session = await client.memory.getSession(session_id); +console.log(session); +``` + + + + +## Deleting a Session + +Deleting a Session soft-deletes the Session and all associated Memories. The Session and Memories are still available in +the database, but are marked as deleted and will not be returned in search results. + +If you persist memory to a deleted Session, it will be undeleted. Deleted Memories will, however, remain deleted. + +Soft-deleted data is hard-deleted periodically. + + + + + +```python +await client.memory.adelete_memory(session_id) +``` + + + + +```typescript +await client.memory.deleteMemory(session_id); +``` + + + + +## Listing Sessions + +You can list all Sessions in the Zep Memory Store with optional limit and cursor parameters for pagination. We also +provider a helper generator function making it simple to iterate over all Sessions. + + + + + +```python +# List the first 10 Sessions +sessions = client.memory.list_sessions(limit=10, cursor=0) +for session in sessions: + print(session) + +# List All Sessions using a generator +all_sessions = client.memory.list_all_sessions(chunk_size=100) +for session_chunk in all_sessions: + for session in session_chunk: + print(session) +``` + + + + +```typescript +// List the first 10 Sessions +const sessions: Session[] = await client.memory.listSessions(10, 0); +console.log("First 10 Sessions:"); +sessions.forEach((session) => console.log(session)); + +// List All Sessions using a generator +console.log("All Sessions:"); +const allSessionsGenerator = client.memory.listSessionsChunked(100); +for await (const sessionChunk of allSessionsGenerator) { + sessionChunk.forEach((session) => console.log(session)); +} +``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/summaries.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/summaries.mdx new file mode 100644 index 00000000000..9eebec0c97c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/chat-history-memory/summaries.mdx @@ -0,0 +1,31 @@ +A summary for the last or trailing `N` messages is generated on every new message added to a Session. `N` is the +configured +`Message Window` for your [Zep Project](/getting-started/projects). The first summary is generated after the first `N` messages are +added to a Session. + + +**Summaries are generated asynchronously.** + +Zep uses a proprietary, low-latency summarization model to generate dialog summaries asynchronously. This means that +summary generation does not delay your requests to retrieve Memories from Zep. + + + + + List of Summaries + + +## Accessing Summaries + +Summaries are added to Memory responses when you retrieve a Session's [Memory](memories), enabling your prompt to be +populated with both the most recent messages and a summary the more distant history. + +### Perpetual Memory + +Zep searches for summaries relevant to the current conversation, summaries high-scoring summaries that are returned from +search and includes this summary in your Memory request. + +### Message Window Buffer Memory + +Zep automatically determines which summary to add to a response based on your configured `Message Window` and the number +of messages in the Session. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/dialog-classification.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/dialog-classification.mdx new file mode 100644 index 00000000000..290c100f8f1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/dialog-classification.mdx @@ -0,0 +1,178 @@ +--- +title: "Dialog Classification" +--- + + + Zep enables you to classify Sessions into various categories and save these classifications in the Session's metadata. + This feature is handy for directing Sessions to appropriate agents or for monitoring the kinds of interactions users + have with your Assistant. Classifications are processed in just a few hundred milliseconds, allowing them to run + synchronously with the chat loop. + +## Classifying Sessions + +A classification task consists of a topic and a list of classes. +The `topic` is the type of classification you want to perform, and the `classes` are the possible categories you want to classify the Session into. +The `topic` is only used as a name or label for the classification task and does not affect the classification itself. + +You may optionally specify the number of previous messages to consider when classifying the Session (default `4`), and whether to persist the classification in the Session's metadata (default `True`). + + + + ```python classes = ["class1", "class2", ..., "other"] classification = client.memory.classify_session( session_id, + "topic", classes, last_n=4, persist=True ) ``` + + + ```typescript const classes = ["class1", "class2", ..., "other"] const classification = await + client.memory.classifySession( session_id, "topic", classes, 4, // lastN true // persist ) ``` + + + +The returned result will be one of the classes you provided, or "other" if none of the classes are a good fit. The classification result is also stored in the Session's metadata if `persist` is `True`. + +```json +{ "system": { "classes": { "topic": "travel" } } } +``` + +And Sessions viewed in the Zep app will be labeled with the classification result. + + + Classifier Metadata Image + + +### Adding Custom Instruction + +You may optionally provide a custom instruction to the classification task. This instruction will be injected into the Dialog Classification Prompt. + +You may want to use this option if you have specific guidelines that you want to communicate to the LLM in addition to our classification prompt. + + + + ```python classes = ["advanced", "beginner", ..., "dojo"] classification = client.memory.classify_session( + session_id, "experience level", classes, instruction="Classify the user's experience level. You may classify + experience as 'dojo' only if the user is a black belt." ) ``` + + + ```typescript const classes = ["advanced", "beginner", ..., "dojo"] const classification = await + client.memory.classifySession( session_id, "experience level", classes, "Classify the user's experience level. You + may classify experience as 'dojo' only if the user is a black belt." ) ``` + + + +### Building a Semantic Router with User Intent + +Zep's Session Classifier can be used to build a semantic router that routes user sessions to different agents or chains based on the user's intent. + +```Text +user: Hello, my phone isn't responding to touch. +``` + + + + ```python + classes = [ + "sales interest", + "needs support", + "has payment question", + "other" + ] + classification = client.memory.classify_session( + session_id, "intent", classes + ) + + print(classification) + ``` + + + ```typescript + const classes = [ + "sales interest", + "needs support", + "has payment question", + "other" + ] + +const classification = await client.memory.classifySession( +session_id, "intent", classes +) + + console.log(classification.class) + ``` + + + +```Text +"needs support" +``` + +### A High-Performance Tool Picker + +Using an agent to pick tools can often be slow and inaccurate. Zep's Session Classifier allows you to pick tools at very low latency +and high accuracy. You may then instruct an LLM to use the selected tool and provided Session information. + +```Text +user: What is the capital of France? +``` + + + + ```python classes = [ "complete math problems using a calculator", "research topics or find information with a web + search", "no matching tool", ] classification = client.memory.classify_session( session_id, "tool", classes ) ``` + + + ```typescript const classes = [ "complete math problems using a calculator", "research topics or find information + with a web search", "no matching tool", ] const classification = await client.memory.classifySession( session_id, + "tool", classes ) ``` + + + +```Text +"research topics or find information with a web search" +``` + +### Classifying Emotions + +Zep's Session Classifier can be used to classify user emotions and store these classifications in the Session's metadata. + +```Text +AI: We're unfortunately going to reschedule your appointment. +user: Is that entirely necessary? I'm very busy. +``` + + + + ```python classes = [ "happy", "sad", "frustrated", "angry", "other" ] classification = + client.memory.classify_session( session_id, "emotion", classes ) ``` + + + ```typescript const classes = [ "happy", "sad", "frustrated", "angry", "other" ] classification = await + client.memory.classifySession( session_id, "emotion", classes ) ``` + + + +```Text +"frustrated" +``` + +## Executing Multiple Classifications + +Zep supports executing multiple classification tasks against a Session, allowing you to classify a Session into different categories and store these classifications in the Session's metadata. + +```json +{ + "system": { + "classes": { + "topic": "travel", + "intent": "needs support", + "emotion": "frustrated" + } + } +} +``` + +## Best Practices + +1. **Use a small number of classes**: The more classes you have, the more difficult it is to classify a Session accurately. Recommendation: no more than 5 or 6 classes. +2. **Ensure your classes are well separated**: If your classes are too similar, the classifier will have a hard time distinguishing between them. +3. **Provide a "none", "other", or "unknown" class**: If none of the classes are a good fit, the classifier should be able to select an option that indicates this. +4. **Limit the number of previous messages**: The more messages you consider, the longer the classification will take. Additionally, the context may change through the conversation. Recommendation: no more than the prior 4 to 6 messages. +5. **Persist the classification**: If you want to use the classification result in the future, you should persist the classification in the Session's metadata. This is the default. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/document-collections.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/document-collections.mdx new file mode 100644 index 00000000000..bed52988367 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/document-collections.mdx @@ -0,0 +1,500 @@ +--- +title: "Document Collections" +--- + +Zep's document vector store lets you embed and search documents using vector similarity search, Maximum Marginal Relevance Re-Ranking, and metadata filtering. + +You can manage collections, ingest documents, and search using _Zep's SDKs_, _LangChain_, or _LlamaIndex_. + + + `zep-python` supports async operations. + + All methods come in sync and async flavors, with async methods prefixed with `a`. + + For instance, `zep-python` offers both `zep_client.memory.add_memory` and `zep_client.memory.aadd_memory`. + + + +## Key Concepts + +### Collections + +A `Collection` is a group of documents that use the same embedding strategy and model. _Zep automatically creates embeddings_ for the documents you provide. + +### Documents + +`Documents` are the texts you want to embed and search. You can add documents to collections and optionally assign them a unique ID and metadata. _If you add metadata, it can help filter search results._ + +## Initializing the Zep Client + +For details on initializing the Zep client, check out the [SDK documentation](sdks). + +## Creating a Collection + + + + + +```python +client = ZepClient(api_key="zep_api_key") + +collection_name = "babbagedocs" # the name of your collection. alphanum values only + +collection = client.document.add_collection( + name=collection_name, # required + description="Babbage's Calculating Engine", # optional + metadata={"foo": "bar"}, # optional metadata to associate with this collection +) +``` + + + + + +```typescript +const client = await ZepClient.init("zep_api_key"); + +const collection_name: string = "babbagedocs"; // The name of your collection. Alphanumeric values only + +const collection = await client.document.addCollection({ + name: collection_name, // Required + description: "Babbage's Calculating Engine", // Optional + metadata: { foo: "bar" }, // Optional metadata to associate with this collection +}); +``` + + + + + +## Loading an Existing Collection + + + + + +```python +collection = client.document.get_collection(collection_name) +``` + + + + + +```typescript +const collection = await client.document.getCollection(collection_name); +``` + + + + + +## Adding Documents to a Collection + + + + + +```python +chunks = read_chunks_from_file(file, max_chunk_size) # your custom function to read chunks from a file + +documents = [ + Document( + content=chunk, + document_id=f"{collection_name}-{i}", # optional document ID + metadata={"bar": i}, # optional metadata + ) + for i, chunk in enumerate(chunks) +] + +uuids = collection.add_documents(documents) +``` + +`document_id` is an optional identifier you can assign to each document. It's handy for linking a document chunk with a specific ID you choose. + +The `metadata` is an optional dictionary that holds metadata related to your document. Zep leverages this metadata for hybrid searches across a collection, enabling you to filter search results more effectively. + +When you use `collection.add_documents`, it returns a list of Zep UUIDs corresponding to the documents you've added to the collection. + + + + + +```typescript +const chunks: string[] = read_chunks_from_file(file, max_chunk_size); // Your custom function to read chunks from a file + +const documents: Document[] = chunks.map((chunk, index) => { + return new Document({ + content: chunk, + documentId: `${collection_name}-${index}`, // Optional document ID + metadata: { bar: index }, // Optional metadata + }); +}); + +const uuids: string[] = await client.document.addDocuments(collection_name, documents); +``` + +`document_id` is an optional identifier you can assign to each document. It's handy for linking a document chunk with a specific ID you choose. + +The `metadata` is an optional dictionary that holds metadata related to your document. Zep leverages this metadata for hybrid searches across a collection, enabling you to filter search results more effectively. + +When you use `collection.addDocuments`, it returns a list of Zep UUIDs corresponding to the documents you've added to the collection. + + + + + +Zep's document vector store has VectorStore support for Langchain. + +**Python Example:** + +```python +from langchain.docstore.base import Document +from langchain.text_splitter import RecursiveCharacterTextSplitter +from zep_python.langchain import ZepVectorStore + +vectorstore = ZepVectorStore(collection) + +text_splitter = RecursiveCharacterTextSplitter( + chunk_size=400, + chunk_overlap=50, + length_function=len, +) + +docs = text_splitter.create_documents([raw_text]) +uuids = vectorstore.add_documents(docs) +``` + +**Typescript Example:** + +```typescript +import { ZepClient } from "@getzep/zep-js"; +import { ZepVectorStore } from "@getzep/zep-js/langchain"; +import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; + +const vectorStore = await ZepVectorStore.init({ + client: zepClient, + collectionName: "", +}); +const text_splitter = new RecursiveCharacterTextSplitter({ + chunkSize: 400, + chunkOverlap: 50, + lengthFunction: (text: string) => text.length, // Assuming lengthFunction accepts a function that returns the length of the text +}); + +const docs = await text_splitter.createDocuments([raw_text]); +const uuids = await vectorstore.addDocuments(docs); +``` + + + + + +### Chunking your documents + +Choosing the right _chunking strategy_ is crucial and highly dependent on your specific needs. A variety of 3rd-party libraries, including Langchain, offer support for processing documents from numerous sources and dividing them into smaller segments suitable for embedding. + +We recommend experimenting with various extractors, chunking strategies, sizes, and overlaps to discover the optimal approach for your project. + +## Monitoring Embedding Progress + +The process of embedding documents in Zep is asynchronous. To keep track of your collection's embedding progress, you can periodically check the collection's status: + + + + + +```python +import time + +while True: + c = client.document.get_collection(collection_name) + print( + "Embedding status: " + f"{c.document_embedded_count}/{c.document_count} documents embedded" + ) + time.sleep(1) + if c.status == "ready": + break +``` + + + + + +```typescript +while (true) { + const collection = await client.document.getCollection(collectionName); + console.log(`Embedding status: ${c.document_embedded_count}/${c.document_count} documents embedded`); + await sleep(1000); // Sleep for 1 second (1000 milliseconds) + if (collection.status === "ready") { + break; + } +} +``` + + + + + +Once the collection's status changes to `ready`, it means all documents have been successfully embedded and are now searchable. + +## Searching a Collection with Hybrid Vector Search + +Zep enables hybrid vector search across your collections, allowing you to pinpoint the most relevant documents based on semantic similarity. Additionally, you have the option to refine your search by filtering through document metadata. + +You can initiate a search using either a text query or an embedding vector, depending on your needs. + + + Zep's Collection and Memory search support semantic search queries, JSONPath-based metadata filters, and a combination of both. Memory search also supports querying by message creation date. + + Read more about [constructing search queries](working-with-search). + + + + + + + +```python +# search for documents using only a query string +query = "the moon" +results = collection.search(text=query, limit=5) + +# hybrid search for documents using a query string and metadata filter +metadata_query = { + "where": {"jsonpath": '$[*] ? (@.baz == "qux")'}, +} +results = collection.search(text=query, metadata=metadata_query, limit=5) + +# Search by embedding vector, rather than text query +# embedding is a list of floats +results = collection.search( + embedding=embedding, limit=5 +) +``` + +`metadata` is an optional dictionary of [JSONPath filters](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html) used to match on metadata associated with your documents. + +`limit` is an optional integer indicating the maximum number of results to return. + + + + + +```typescript +const query = "the moon"; +let results = await collection.search({ text: query, limit: 5 }); + +// Hybrid search for documents using a query string and metadata filter +const metadataQuery = { + where: { jsonpath: '$[*] ? (@.baz == "qux")' }, +}; +results = await collection.search({ text: query, metadata: metadataQuery, limit: 5 }); + +// Search by embedding vector, rather than text query +// embedding is a list of floats +results = await collection.search({ embedding: embedding, limit: 5 }); +``` + +`metadata` is an optional dictionary of [JSONPath filters](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html) used to match on metadata associated with your documents. + +`limit` is an optional integer indicating the maximum number of results to return. + + + + +**Python Example:** + +```python +query = "What is Charles Babbage best known for?" + +print(f"\nSearching for '{query}'\n") +results = vectorstore.search(query, search_type="similarity", k=5) +print_results(results) + +print(f"\nSearching for '{query}' with MMR reranking\n") +results = vectorstore.search(query, search_type="mmr", k=5) +print_results(results) +``` + +**Typescript Example:** + +```typescript +const query = "What is Charles Babbage best known for?"; + +console.log(`\nSearching for '${query}'\n`); +const resultsSimilarity = await vectorstore.search(query, { searchType: "similarity", k: 5 }); +console.log(resultsSimilarity); + +console.log(`\nSearching for '${query}' with MMR reranking\n`); +const resultsMMR = await vectorstore.search(query, { searchType: "mmr", k: 5 }); +console.log(resultsMMR); +``` + +_You can also use `ZepVectoreStore` as a retriever with langchain_ + +Python example + +```python +retriever = vectorstore.as_retriever() +_inputs = RunnableParallel( + { + "question": lambda x: x["question"], + "context": extract_question | retriever | _combine_documents, + }, +).with_types(input_type=UserInput) + +chain = (_inputs | answer_prompt | ChatOpenAI() | StrOutputParser()) +``` + + **Please check the complete python [rag vector store chain example](https://github.com/getzep/zep-python/blob/main/examples/langchain-langserve/app/rag_vector_store_chain.py)** + +Typescript example + +```typescript +const retriever = vectorStore.asRetriever(); + +const setupAndRetrieval = RunnableMap.from({ + context: new RunnableLambda({ + func: (input: string) => retriever.invoke(input).then(combineDocuments), + }), + question: new RunnablePassthrough(), +}); +const outputParser = new StringOutputParser(); + +const chain = setupAndRetrieval.pipe(prompt).pipe(model).pipe(outputParser); +``` + +> _Please check the complete typescript [rag vector store chain example](https://github.com/getzep/zep-js/blob/main/examples/langchain/vector_store_example.ts)_ + + + + + +## Retrieving Documents by UUID + +Zep supports retrieving a list of documents by Zep UUID: + + + + + +```python +docs_to_get = uuids[40:50] +documents = collection.get_documents(docs_to_get) +``` + + + + + +```typescript +const docsToGet = uuids.slice(40, 50); +const documents = await collection.getDocuments(docsToGet); +``` + + + + + +## Other Common Operations + +This section covers additional common operations you might need to perform, such as listing all collections within your client's scope. The examples above demonstrate how to create an index on a collection and list all collections for both Python and TypeScript. + +### Updating a Collection's Description or Metadata + + + + + +```python +client.document.update_collection( + collection_name, + description="Charles Babbage's Babbage's Calculating Engine 2", + metadata={"newfoo": "newbar"}, +) +``` + + + + +```typescript +await client.document.updateCollection(collectionName, { + description: "Charles Babbage's Babbage's Calculating Engine 2", + metadata: { newfoo: "newbar" }, +}); +``` + + + + + +### Update a Document's ID or Metadata + + + + + +```python +collection.update_document(document_uuid, document_id="new_id", metadata={"foo": "bar"}) +``` + + + + +```typescript +await collection.updateDocument(documentUUID, { documentId: "new_id", metadata: { foo: "bar" } }); +``` + + + + + +### Deleting Documents + +Zep supports deleting documents from a collection by UUID: + + + + + +```python +collection.delete_document(document_uuid) +``` + + + + + +```typescript +await collection.deleteDocument(document_uuid); +``` + + + + + +### Deleting a Collection + +Deleting a collection will delete all documents in the collection, as well as the collection itself. + + + + + +```python +client.document.delete_collection(collection_name) +``` + + + + + +```typescript +await client.document.deleteCollection(collection_name); +``` + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/chainlit.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/chainlit.mdx new file mode 100644 index 00000000000..febef65e9ae --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/chainlit.mdx @@ -0,0 +1,144 @@ +--- +title: "Chainlit" +description: "Chainlit is an open-source async Python framework which allows developers to build scalable Conversational AI or agentic applications." +--- + + + You can follow Chainlit installation steps on their + Getting Started Page + + +By integrating Zep into your Chainlit LLM application, you elevate your conversational agent with powerful features like long-term memory and context fusion. + +In this guide, we'll walk you through the steps to build a simple Question and Answer agent using Chainlit, Open AI and Zep. + +### Steps to Use Zep Cloud with ChainLit + +1. **Setup Zep Client**: Initialize the Zep Client within your ChainLit application using your [Zep Project API key](https://help.getzep.com/projects). + +```python +# Import necessary modules from Zep Python SDK and ChainLit. +from zep_python import ZepClient +from zep_python.memory import Memory, Session +from zep_python.message import Message +from zep_python.user import CreateUserRequest +import chainlit as cl +import uuid +import os +from openai import AsyncOpenAI + +# Retrieve API keys from environment variables. +ZEP_API_KEY = os.environ.get("ZEP_API_KEY") +OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") + +# Initialize clients for OpenAI GPT-4 and Zep with respective API keys. +openai_client = AsyncOpenAI(api_key=OPENAI_API_KEY) +zep = ZepClient(api_key=ZEP_API_KEY) +``` + +2. **User and Session Management**: Leverage the `CreateUserRequest` and [`Session`](https://help.getzep.com/chat-history-memory/sessions) models to manage your application's users and sessions effectively. + +```python +@cl.on_chat_start +async def start_chat(): + """Handles the event triggered at the start of a new chat through ChainLit.""" + # Generate unique identifiers for the user and session. + user_id = str(uuid.uuid4()) + session_id = str(uuid.uuid4()) + + # Save user and session identifiers in the current session context. + cl.user_session.set("user_id", user_id) + cl.user_session.set("session_id", session_id) + + # Register a new user in Zep's system using the generated User ID. + await zep.user.aadd(CreateUserRequest(user_id=user_id)) + + # Start a new session for the user in Zep. + await zep.memory.aadd_session(Session(user_id=user_id, session_id=session_id)) +``` + +3. **Zep Dialog tools**: Elevate agent knowledge with ChainLit Steps and Zep Dialog Tools + + Discover more about Zep's dialog tools on the{" "} + Zep Documentation Page. + + +```python +@cl.step(name="session classification", type="tool") +async def classify_session(session_id: str): + """Classify dialog with custom instructions.""" + # Define categories for classification. + classes = [ + "General", + "Travel", + "Shopping", + "Cars", + ] + # Use Zep's dialog async classification feature with custom instruction for session classification. + classification = await zep.memory.aclassify_session( + session_id, "session_classification", classes, persist=True, instruction="you are a helpful assistance, give a conversation classify 0 for General topics, 1 for Travel-related discussions, 2 for Shopping conversations, and 3 for talks about Cars. For example, a chat about visiting Paris for landmarks and cuisine should be classified as 1." + ) + return classification +``` + +4. **Message Handling**: You can effectively store and fetch your Chainlit application chat history on Zep memory store, enhancing your LLM conversational context. + + + Discover more about Zep's memory store capabilities on the{" "} + Zep Documentation Page. + + +```python + +@cl.step(name="OpenAI", type="llm") +async def call_openai(session_id): + """Invokes the OpenAI API to generate a response based on the session message history.""" + # Fetch session messages from Zep. + memory = await zep.message.aget_session_messages(session_id) + memory_history = [m.to_dict() for m in memory] + + # Prepare data, excluding certain fields for privacy/security. + cleaned_data = [{k: v for k, v in item.items() if k not in ['created_at', 'role_type', 'token_count', 'uuid']} for item in memory_history] + + # Generate a response from OpenAI using the cleaned session data. + response = await openai_client.chat.completions.create( + model="gpt-4", + temperature=0.1, + messages=cleaned_data, + ) + return response.choices[0].message + +@cl.on_message +async def on_message(message: cl.Message): + """Processes each incoming message, integrates with OpenAI for response, and updates Zep memory.""" + session_id = cl.user_session.get("session_id") + # classify user message to give the LLM a semantic insights to what the user request is about + classify_sess = await classify_session(session_id) + # Store the incoming message in Zep's session memory and append the classified dialog. + await zep.memory.aadd_memory( + session_id, + Memory(messages=[Message(role_type="user", content=message.content + "\n" + "conversation_topic: " + classify_sess.class_, role="user")]), + ) + + # Retrieve a response from the OpenAI model. + response_message = await call_openai(session_id) + + # Send the generated response back through ChainLit. + msg = cl.Message(author="Answer", content=(response_message.content)) + await msg.send() + + # Update Zep's session memory with the assistant's response for continuity. + await zep.memory.aadd_memory( + session_id, + Memory(messages=[Message(role_type="assistant", content=response_message.content, role="assistant")]), + ) + +``` + +5. To access your LLM session data, navigate to the Zep Cloud Console, select a session, and review all the associated session data and logs. + +Zep Cloud session console example + +In conclusion, integrating Zep Cloud with Chainlit empowers developers to create conversational AI applications that are more intelligent, context-aware, and efficient. + +For additional examples, check out more [use cases](https://github.com/getzep/zep-python/tree/main/examples). diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/flowise.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/flowise.mdx new file mode 100644 index 00000000000..d2d529b3f7f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/flowise.mdx @@ -0,0 +1,53 @@ +--- +title: "Flowise" +description: "Flowise is a low-code/no-code drag & drop tool with the aim to make it easy for people to visualize and build LLM apps." +--- + + + You can follow Flowise installation steps on their + Getting Started Page + + +## Using Zep Cloud Nodes + +Once you have Flowise app up and running, you can use Zep Cloud nodes in your LLM apps. + +We expose the following nodes: + +- `Zep Memory - Cloud` - wrapper around Zep Memory API. You can use this node to add Long-Term memory to your app. + Learn more about [Zep Memory](/chat-history-memory) + +In order to use this node, you will need to have a Zep Project API key and add it as a Connect Credential in your Flowise app. + +Zep Cloud Memory Flowise Node + +A complete chain using Zep Memory Node, may look like this: + +Zep Cloud Memory Flowise Node Complete Example + +- `Zep Collection - Cloud` - wrapper around Zep’s document vector store. It lets you embed and search documents. You can use this node to add RAG capabilities to your app. + Learn more about [Zep Collections](/document-collections) + +In order to use this node, you will need to have a Zep Project API key and add it as a Connect Credential in your Flowise app. + +You will also need to provide the collection name for your Zep Collection. + +Zep Cloud Collection Flowise Node + +A complete chain using Zep Collection Node, may look like this: + + + Note that we are using a Website Crawler node to load documents about **Marie Curie** into a **Zep Collection**. + You can also connect any other Document Loader available on Flowise platform. + + We are using **Zep Collection Node** as a retriever passed into **Conversational Retrieval QA Chain**. + + This example also illustrates how you can use **Zep Memory** in combination with **Zep Collection**. + + + +Zep Cloud Collection Flowise Node Complete Example diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/langflow.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/langflow.mdx new file mode 100644 index 00000000000..3c6bdda6732 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/langflow.mdx @@ -0,0 +1,4 @@ +--- +title: "LangFlow" +description: "Zep Cloud Integration coming soon" +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/llamaindex.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/llamaindex.mdx new file mode 100644 index 00000000000..37aa31ff4f4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/llamaindex.mdx @@ -0,0 +1,4 @@ +--- +title: "LlamaIndex" +description: "Zep Cloud Integration coming soon" +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/n8n.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/n8n.mdx new file mode 100644 index 00000000000..8e7d7bafd35 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ecosystem/n8n.mdx @@ -0,0 +1,4 @@ +--- +title: "n8n" +description: "Zep Cloud Integration coming soon" +--- diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ellipsis.yaml b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ellipsis.yaml new file mode 100644 index 00000000000..88cc49b82bf --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/ellipsis.yaml @@ -0,0 +1,9 @@ +version: 1.2 +pr_review: + confidence_threshold: 0.5 + rules: + - Ensure you check grammar and spelling. Suggest alternatives if needed. + - Tone should be appropriate for a developer audience. + - Review code blocks for syntax and logical errors. +pr_address_comments: + delivery: "new_pr" diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/faq.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/faq.mdx new file mode 100644 index 00000000000..2c0079793f9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/faq.mdx @@ -0,0 +1,47 @@ +--- +title: "FAQ" +--- + + + Yes - Zep offers a free tier. See [Service Plans and Limits](/service-plans) for more information. + + + Yes - Zep offers a LangChain `ZepChatMessageHistory` and `ZepVectorStore` class that you may use in your LangChain + Expression Language (LCEL) project. + + Please use the classes available in the Cloud SDKs and not in the LangChain SDKs. + + + + Not yet - we are working on adding support for LlamaIndex. + + + Yes, you can use `Zep Memory - Cloud` and `Zep Collection - Cloud` nodes in the latest Flowise release. + + You can learn more in our [Flowise Ecosystem section](/ecosystem/flowise). + + + + + Yes - we've deprecated Intent Analysis and replaced it with a [Session Classifier](/dialog-classification). + + We've also removed the Named Entity Recognition (NER) feature. + We are in the process of replacing NER with a more powerful and flexible Structured Data Extractor. + + + + The API URL for Zep Cloud is `https://api.getzep.com`. Note that you do not need to specify the API URL when using the Cloud SDKs. + If a service requests the Zep URL it is possible it's only compatible with the Zep Open Source service. + + + Currently, Zep Cloud only supports English. We're working on adding support for other languages. Contact us if you're + interested in a specific language. + + + Zep Cloud is currently a major update to the Zep Open Source product. Going forward, Zep Open Source will offer a + subset of Zep Cloud's functionality, primarily as proprietary language models are used exclusively in Zep Cloud. + + + Yes - we will be releasing a new version of Zep Open Source based on Zep Cloud functionality. + See above for more. + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/getting-support.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/getting-support.mdx new file mode 100644 index 00000000000..aaabb6cc732 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/getting-support.mdx @@ -0,0 +1,3 @@ +# Getting Support + +Use the in-app chat to get help from the Zep team. You may also email us at [success@getzep.com](mailto:success@getzep.com). diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/classifier-metadata.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/classifier-metadata.png new file mode 100644 index 00000000000..88291b8ab07 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/classifier-metadata.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/create_new_project.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/create_new_project.png new file mode 100644 index 00000000000..b52f286d1ae Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/create_new_project.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/fact-table.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/fact-table.png new file mode 100644 index 00000000000..0fd8b722524 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/fact-table.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/favicon.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/favicon.png new file mode 100644 index 00000000000..88487ef07a6 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/favicon.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-collection-node-complete.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-collection-node-complete.png new file mode 100644 index 00000000000..f8f9b3e363a Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-collection-node-complete.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-collection-node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-collection-node.png new file mode 100644 index 00000000000..4d95c1f41dd Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-collection-node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-memory-node-complete.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-memory-node-complete.png new file mode 100644 index 00000000000..9d33d9ae51b Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-memory-node-complete.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-memory-node.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-memory-node.png new file mode 100644 index 00000000000..bb0cde68a84 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/flowise-memory-node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/perpetual_memory.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/perpetual_memory.png new file mode 100644 index 00000000000..697f62d19c5 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/perpetual_memory.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/session-chainlit-ecosystem.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/session-chainlit-ecosystem.png new file mode 100644 index 00000000000..c7106d4c17d Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/session-chainlit-ecosystem.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/summaries.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/summaries.png new file mode 100644 index 00000000000..862a37d4786 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/summaries.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/zep-bot-square-200x200.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/zep-bot-square-200x200.png new file mode 100644 index 00000000000..11b39aaf43c Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/images/zep-bot-square-200x200.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/messagehistory-example.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/messagehistory-example.mdx new file mode 100644 index 00000000000..a3795d04955 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/messagehistory-example.mdx @@ -0,0 +1,247 @@ +--- +title: "MessageHistory Example" +--- + +The Zep Python SDK includes the `ZepChatMessageHistory` class, compatible with [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/expression_language/get_started). + +This guide will walk you through creating a [MessageHistory](/chat-history-memory) chain using Zep's conversation history. + +**You can generate a project api key in [Zep Dashboard](https://app.getzep.com/projects).** + + + Make sure you have the following environment variables specified when running these examples: + + `ZEP_API_KEY` - API key to your zep project + + `OPENAI_API_KEY` - Open AI api key which the chain will require to generate the answer + + + + + **You will need to have a collection in place to initialize vector store in this example** + + If you want to create a collection from a web article, + you can run the [python ingest script](https://github.com/getzep/zep-python/blob/main/examples/langchain-langserve/app/ingest.py) + Try modifying the script to ingest the article of your choice. + + Alternatively, you can create a collection by running either [Document example](https://github.com/getzep/zep-python/blob/main/examples/documents/documents_async.py) + in python sdk repository or [Document example](https://github.com/getzep/zep-js/blob/main/examples/documents/index.ts) in typescript sdk repository. + + + + + **You will need to have a `session_id` in place to invoke the final chain in this example** + + You can create a session by running either [Memory example](https://github.com/getzep/zep-python/blob/main/examples/chat_history/memory_async.py) + + in python sdk repository or [Memory example](https://github.com/getzep/zep-js/blob/main/examples/memory/memory_example.ts) in typescript sdk repository. + + + +**Initialize ZepClient with necessary imports** + + + + + +```python +import os +from typing import List, Tuple + +from langchain_core.output_parsers import StrOutputParser +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +from langchain_core.pydantic_v1 import BaseModel, Field +from langchain_core.runnables import ( + RunnableParallel, +) +from langchain_core.runnables.history import RunnableWithMessageHistory +from langchain_openai import ChatOpenAI + +from zep_python import ZepClient +from zep_python.langchain import ZepChatMessageHistory + +zep = ZepClient( + api_key=ZEP_API_KEY, +) +``` + + + + + +```typescript +import { ZepClient } from "@getzep/zep-js"; +import { ZepChatMessageHistory } from "@getzep/zep-js/langchain"; +import { ChatOpenAI } from "@langchain/openai"; +import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; +import { RunnableWithMessageHistory } from "@langchain/core/runnables"; + +const zepClient = await ZepClient.init(process.env.ZEP_API_KEY); +``` + + + + + + + + +```python +template = """Answer the question below as if you were a 19th centry poet: + """ +answer_prompt = ChatPromptTemplate.from_messages( + [ + ("system", template), + MessagesPlaceholder(variable_name="chat_history"), + ("user", "{question}"), + ] +) +``` + + + + +```typescript +const prompt = ChatPromptTemplate.fromMessages([ + ["system", "Answer the user's question below. Be polite and helpful:"], + new MessagesPlaceholder("history"), + ["human", "{question}"], +]); +``` + + + + +**Set up an answer synthesis template and prompt.** + + + **`MessagesPlaceholder`** - We're using the variable name `chat_history` here. + + This will incorporate the chat history into the prompt. + + It's **important** that this variable name aligns with the `history_messages_key` in the `RunnableWithMessageHistory` chain for seamless integration. + + + + **`question` must match `input_messages_key` in `RunnableWithMessageHistory`` chain.** + +**Compose the final chain** + + + + + +```python +inputs = RunnableParallel( + { + "question": lambda x: x["question"], + "chat_history": lambda x: x["chat_history"], + }, +) + +chain = RunnableWithMessageHistory( + inputs | answer_prompt | ChatOpenAI() | StrOutputParser(), + lambda session_id: ZepChatMessageHistory( + session_id=session_id, # This uniquely identifies the conversation + zep_client=zep, + memory_type="perpetual", + ), + input_messages_key="question", + history_messages_key="chat_history", +) +``` + + + + +```typescript +const chain = prompt.pipe( + new ChatOpenAI({ + temperature: 0.8, + modelName: "gpt-3.5-turbo-1106", + }), +); + +const chainWithHistory = new RunnableWithMessageHistory({ + runnable: chain, + getMessageHistory: (sessionId) => + new ZepChatMessageHistory({ + client: zepClient, + sessionId: sessionId, + memoryType: "perpetual", + }), + inputMessagesKey: "question", + historyMessagesKey: "history", +}); +``` + + + + +Here's a quick overview of what's happening: + +1. We use `RunnableWithMessageHistory` to incorporate [Zep's Chat History](/chat-history-memory) into our chain. +2. This class requires a `session_id` as a parameter when you activate the chain. +3. To manually invoke this chain, provide the `session_id` as a parameter and the `question` as an input to the chain. + + + + + +```python +chain_with_history.invoke( + {"question": "-"}, + config={"configurable": {"session_id": "-"}}, +) +``` + + + + +```typescript +await chainWithHistory.invoke( + { + question: "-", + }, + { + configurable: { + sessionId: "-", + }, + }, +); +``` + + + + +First, we initialize `ZepChatMessageHistory` with the following parameters: + +1. `session_id` - This uniquely identifies the conversation within Zep. +2. `zep_client` - The instance of the Zep client. +3. `memory_type` set to `perpetual`. If not specified, Message Window Buffer Memory will be used by default. We recommend configuring your application to use Perpetual Memory. + +Interested in learning more? [Explore How Zep Memory Works](/chat-history-memory). + +Next, we construct a chain that operates after retrieving the chat history: + +1. `inputs` will extract the user's question and chat history from the context. +2. `answer_prompt` will incorporate chat history into the prompt. +3. `ChatOpenAI` will generate a response. +4. `StrOutputParser` will parse the response. + +## Running the Chain with LangServe + +This chain can also be executed as part of our LangServe sample project. To do this, you'll need to: + +For this you will need to: + +Clone our [Python SDK](https://github.com/getzep/zep-python) + +```bash +git clone https://github.com/getzep/zep-python +cd examples/langchain-langserve +``` + +There is a [README](https://github.com/getzep/zep-python/blob/main/examples/README.md) file in the `langchain-langserve` directory will guide you through the setup process. + +Go to `http://localhost:8000/message_history/playground` to use LangServe playground for this chain. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/rag-message-history-example.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/rag-message-history-example.mdx new file mode 100644 index 00000000000..c8b7af5f408 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/rag-message-history-example.mdx @@ -0,0 +1,31 @@ +--- +title: "RAG + Message History example" +--- + +**You can generate a Project API Key in [Zep Dashboard](https://app.getzep.com/projects).** + + + Make sure you have the following environment variables specified when running these examples: + + `ZEP_API_KEY` - API key to your zep project + + `OPENAI_API_KEY` - Open AI api key which the chain will require to generate the answer + + + + + **You will need to have a `session_id` in place to invoke the final chain in this example** + + You can create a session by running either [Memory example](https://github.com/getzep/zep-python/blob/main/examples/chat_history/memory_async.py) + + in python sdk repository or [Memory example](https://github.com/getzep/zep-js/blob/main/examples/memory/memory_example.ts) in typescript sdk repository. + + + +You can find a complete RAG + Message History examples in the SDK examples + +They showcase how to use the `ZepChatMessageHistory` and `ZepVectorStore` classes as part of one chain. + +[Python RAG + Message history example](https://github.com/getzep/zep-python/tree/main/examples/langchain-langserve) + +[Typescript RAG + Message history example](https://github.com/getzep/zep-js/blob/main/examples/langchain/message_history_vector_store_example.ts) diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/vectorstore-example.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/vectorstore-example.mdx new file mode 100644 index 00000000000..4b05d6e9786 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/examples/vectorstore-example.mdx @@ -0,0 +1,334 @@ +--- +title: "VectorStore Example" +--- + +Zep Python SDK ships with `ZepVectorStore` class which can be used with [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/expression_language/get_started) + +Let's explore how to create a RAG chain using the `ZepVectorStore` for semantic search. + +**You can generate a project api key in [Zep Dashboard](https://app.getzep.com/projects).** + + + Before diving into these examples, please ensure you've set the following environment variables: + + `ZEP_API_KEY` - API key to your zep project + + `OPENAI_API_KEY` - Open AI api key which the chain will require to generate the answer + + + + + **You will need to have a collection in place to initialize vector store in this example** + + If you want to create a collection from a web article, + you can run the [python ingest script](https://github.com/getzep/zep-python/blob/main/examples/langchain-langserve/app/ingest.py) + Try modifying the script to ingest the article of your choice. + + Alternatively, you can create a collection by running either [Document example](https://github.com/getzep/zep-python/blob/main/examples/documents/documents_async.py) + in python sdk repository or [Document example](https://github.com/getzep/zep-js/blob/main/examples/documents/index.ts) in typescript sdk repository. + + + + + + + +```python +ZEP_API_KEY = os.environ.get("ZEP_API_KEY") +ZEP_COLLECTION_NAME = os.environ.get("ZEP_COLLECTION") +``` + + + + +```typescript +const ZEP_API_KEY = process.env.ZEP_API_KEY; +const ZEP_COLLECTION_NAME = process.env.ZEP_COLLECTION; +``` + + + + +**Need a project API key? Create one from the [Zep Dashboard](https://app.getzep.com/projects).** + +Initialize ZepClient with necessary imports + + + + + +```python +import os +from typing import List + +from langchain.schema import format_document +from langchain_core.documents import Document +from langchain_core.output_parsers import StrOutputParser +from langchain_core.prompts import ChatPromptTemplate +from langchain_core.prompts.prompt import PromptTemplate +from langchain_core.pydantic_v1 import BaseModel +from langchain_core.runnables import ( + ConfigurableField, + RunnableParallel, +) +from langchain_core.runnables.utils import ConfigurableFieldSingleOption +from langchain_openai import ChatOpenAI + +from zep_python import ZepClient +from zep_python.langchain import ZepVectorStore + +zep = ZepClient( + api_key=ZEP_API_KEY, +) +``` + + + + +```typescript +import { ZepClient } from "@getep/zep-js"; +import { ChatOpenAI } from "@langchain/openai"; +import { BasePromptTemplate, ChatPromptTemplate, PromptTemplate } from "@langchain/core/prompts"; +import { ZepVectorStore, formatDocument } from "@getep/zep-js/langchain"; +import { Document } from "@langchain/core/documents"; +import { RunnableMap, RunnableLambda, RunnablePassthrough } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { ConsoleCallbackHandler } from "@langchain/core/tracers/console"; + +const zepClient = await ZepClient.init(ZEP_API_KEY); +``` + + + + +Initialize ZepVectorStore + + + + + +```python +vectorstore = ZepVectorStore( + collection_name=ZEP_COLLECTION_NAME, + zep_client=zep, +) +``` + + + + +```typescript +const vectorStore = await ZepVectorStore.init({ + client: zepClient, + collectionName: ZEP_COLLECTION_NAME, +}); +``` + + + + +Let's set up the retriever. We'll use `vectorstore` for this purpose and configure it to use [MMR](Search.md) search result reranking. + + + + + +```python +retriever = vectorstore.as_retriever() +``` + + + + + +```typescript +const retriever = vectorStore.asRetriever(); +``` + + + + +Create a prompt template for synthesizing answers. + + + + + +```python +template = """Answer the question based only on the following context: + + {context} + """ +answer_prompt = ChatPromptTemplate.from_messages( + [ + ("system", template), + ("user", "{question}"), + ] +) +``` + + + + +```python +const prompt = ChatPromptTemplate.fromMessages([ + [ + "system", + `Answer the question based only on the following context: + {context}`, + ], + [ + "human", + "{question}" + ], +]); +``` + + + + +Create the default document prompt and define the helper function for merging documents. + + + + + +```python +DEFAULT_DOCUMENT_PROMPT = PromptTemplate.from_template(template="{page_content}") + +def _combine_documents( + docs: List[Document], + document_prompt: PromptTemplate = DEFAULT_DOCUMENT_PROMPT, + document_separator: str = "\n\n", +): + doc_strings = [format_document(doc, document_prompt) for doc in docs] + return document_separator.join(doc_strings) +``` + + + + +```typescript +const DEFAULT_DOCUMENT_PROMPT = PromptTemplate.fromTemplate("{pageContent}"); + +async function combineDocuments( + docs: Document[], + documentPrompt: BasePromptTemplate = DEFAULT_DOCUMENT_PROMPT, + documentSeparator: string = "\n\n", +) { + const docStrings: string[] = await Promise.all( + docs.map((doc) => { + return formatDocument(doc, documentPrompt); + }), + ); + return docStrings.join(documentSeparator); +} +``` + + + + + + + +Let's set up user input and the context retrieval chain. + +```python +# User input +class UserInput(BaseModel): + question: str + +inputs = RunnableParallel( + {"question": lambda x: x["question"], "context": retriever | _combine_documents}, +).with_types(input_type=UserInput) +``` + + + +Define context retriever chain with output parser + +```typescript +const outputParser = new StringOutputParser(); + +const setupAndRetrieval = RunnableMap.from({ + context: new RunnableLambda({ + func: (input: string) => retriever.invoke(input).then(combineDocuments), + }), + question: new RunnablePassthrough(), +}); +``` + + + + +Compose final chain + + + + + +```python +chain = inputs | answer_prompt | ChatOpenAI() | StrOutputParser() +``` + + + + +```typescript +const chain = setupAndRetrieval + .pipe(prompt) + .pipe(model) + .pipe(outputParser) + .withConfig({ + callbacks: [new ConsoleCallbackHandler()], + }); // Optional console callback handler if you want to see input and output of each step in the chain +``` + + + + +Here's a quick rundown of how the process works: + +1. `inputs` grabs the user's question and fetches relevant document context to add to the prompt. +2. `answer_prompt` then takes this context and question, combining them in the prompt with instructions to answer the question using only the provided context. +3. `ChatOpenAI` calls an OpenAI model to generates an answer based on the prompt. +4. Finally, `StrOutputParser` extracts the LLM's result into a string. + +To invote this chain manually, simply pass the `question` into the chain's input. + + + + + +```python +chain_with_history.invoke( + {"question": "-"}, +) +``` + + + + +```typescript +const result = await chain.invoke("-"); // Pass the question as input +``` + + + + +## Running the Chain with LangServe + +You can run this chain, along with others, using our LangServe sample project. + +Here's what you'll need to do: + +Clone our [Python SDK](https://github.com/getzep/zep-python) + +```bash +git clone https://github.com/getzep/zep-python +cd examples/langchain-langserve +``` + +Review the [README](https://github.com/getzep/zep-python/blob/main/examples/README.md) in the `langchain-langserve` directory for setup instructions. + +After firing up the server, head over to `http://localhost:8000/rag_vector_store/playground` to explore the LangServe playground using this chain. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/messagehistory.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/messagehistory.mdx new file mode 100644 index 00000000000..af3df1bf73b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/messagehistory.mdx @@ -0,0 +1,81 @@ +--- +title: "ZepChatMessageHistory" +--- + + + **You can find a complete [Message History Example](examples/messagehistory-example) that showcases how to use the + `ZepChatMessageHistory` class in the chain.** + + + + + + + The Zep Python SDK includes the `ZepChatMessageHistory` class, compatible with [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/expression_language/get_started). + + It expects the following initialization parameters: + + + The unique identifier for Zep Session. + If a session for the provided identifier doesn't exist, one will be created + + + + The Zep client used for making API requests. + You can pass it rather than the API Key. + + + + Zep Project API Key. + You can pass it rather than `zep_client` and we will initialize `ZepClient` for you. + + + + The type of memory to use. + Can be `"message_window"` (the default value if None provided) or `"perpetual"`. + You can find out more about the memory types in the [Chat History documentation](Chat-Histories.md). + + + + ```python Initialization Example + chat_history = ZepChatMessageHistory( + session_id=session_id, + zep_client=client, + memory_type="perpetual", + ) + ``` + + + + + The Zep JS SDK includes the `ZepChatMessageHistory` class, compatible with [LangChain Expression Language (LCEL)](https://js.langchain.com/docs/expression_language/). + + It expects the following initialization parameters: + + + The unique identifier for Zep Session. + If a session for the provided identifier doesn't exist, one will be created. + + + + The Zep client used for making API requests. + You can pass it rather than the API Key. + + + + The type of memory to use. + Can be `"message_window"` or `"perpetual"`. + You can find out more about the memory types in the [Chat History documentation](Chat-Histories.md). + + + + ```typescript Initialization Example + const history = new ZepChatMessageHistory({ + client: zepClient, + sessionId: sessionId, + memoryType: "perpetual", + }) + ``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/overview.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/overview.mdx new file mode 100644 index 00000000000..e25a1824a54 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/overview.mdx @@ -0,0 +1,32 @@ +--- +title: "Overview" +--- + +Zep's [Python](https://github.com/getzep/zep-python) and [Typescript](https://github.com/getzep/zep-js) SDKs ship with [ZepVectorStore](vectorstore) and [ZepChatMessageHistory](messagehistory) classes that are compatible with Langchain's JS and Python Expression Languages. + +### Managing Chat History Memory + +Zep's `ZepChatMessageHistory` class can be used to provide long-term memory for your LangChain chat apps or agents. Zep will store the entire historical message stream, and automatically enrich chat sessions. + +You can also provide your bot or agent with access to relevant messages in long-term storage by using Zep's built-in search. + +### Building Retrieval Augmented Generation Apps (Q&A over Docs) + +Zep's `ZepVectorStore` class can be used to store a collection of documents, metadata, and related embeddings. Retrieval Augmented Generation (RAG) apps can then use Zep's vector search to surface documents relevant to a prompt. + +Zep will automatically embed the documents using low-latency local models, ensuring that your app is fast and responsive. + +## Using Zep as a VectorStore and Document Retriever + +See [Python and Typescript examples](examples/vectorstore-example) of how to use **ZepVectorStore** with LangChain Expression Language in your application. + +## Using Zep as a LangChain Memory Store + +See [Python and Typescript examples](examples/messagehistory-example) of how to use **ZepChatMessageHistory** with LangChain Expression Language in your application. + + + **Want to use `ZepChatMessageHistory` together with `ZepVectorStore`?** + + Please check our complete [Python and Typescript examples](examples/rag-message-history-example) + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/vectorstore.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/vectorstore.mdx new file mode 100644 index 00000000000..de2d70a9747 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/langchain/vectorstore.mdx @@ -0,0 +1,84 @@ +--- +title: "ZepVectorStore" +--- + + + **You can find a complete [Vector Store Example](examples/vectorstore-example) that showcases how to use the + `ZepVectorStore` class in the chain.** + + + + + + + The Zep Python SDK includes the `ZepVectorStore` class, compatible with [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/expression_language/get_started). + + It expects the following initialization parameters: + + + The name of the collection in the Zep Store. + + + + The description of the collection. + + + + The metadata to associate with the collection. + + + + The Zep client used for making API requests. You can pass it rather than the API Key. + + + + Zep Project API Key. You can pass it rather than `zep_client` and we will initialize `ZepClient` for you. + + + + ```python Initialization Example + vectorstore = ZepVectorStore( + collection_name=ZEP_COLLECTION_NAME, + zep_client=zep, + ) + ``` + + + + + The Zep JS SDK includes the `ZepVectorStore` class, compatible with [LangChain Expression Language (LCEL)](https://js.langchain.com/docs/expression_language/). + + It expects the following initialization parameters: + + + The name of the collection in the Zep Store. + + + + The description of the collection. + + + + The metadata to associate with the collection. + + + + The Zep client used for making API requests. + + + + ```typescript Initialization Example + const vectorStore = await ZepVectorStore.init({ + client: zepClient, + collectionName: "testcollection", + }); + ``` + + **We also expose `fromDocuments` and `fromTexts` static methods to initialize `ZepVectorStore` extending Langchain's `VectorStore` interface.** + + In order to initialize using these methods instead of `init`, make sure you are passing + `FakeEmbeddings` as `embeddings` argument as we don't expose the ability to provide your own embeddings on Cloud Version of our SDK. + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/privacy-policy.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/privacy-policy.mdx new file mode 100644 index 00000000000..238b85c430b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/privacy-policy.mdx @@ -0,0 +1,196 @@ +--- +title: "Privacy Policy" +--- + +**Version 1.0** + +**Last revised on: January 27^th^, 2024** + +Zep Software, Inc. (the "Company") is committed to maintaining robust +privacy protections for its users.  Our Privacy Policy ("Privacy +Policy") is designed to help you understand how we collect, use and +safeguard the information you provide to us and to assist you in making +informed decisions when using our Service. + +For purposes of this Agreement, "Site" refers to the Company's website +properties, which can be accessed at the getzep.com Internet domain. + +"Service" refers to the Company's services accessed via the Site, in +which users can view Company marketing material, register for the +Company's services, access support and help resources, and other +services and resources that may be made available from time to time. + +The terms "we," "us," and "our" refer to the Company. + +"You" refers to you, as a user of our Site or our Service. + +By accessing our Site or our Service, you accept our Privacy Policy and +[Terms of Use](website-terms-of-use), and you +consent to our collection, storage, use and disclosure of your Personal +Information as described in this Privacy Policy. + +1. INFORMATION WE COLLECT + +We collect "Non-Personal Information" and "Personal Information." +**Non-Personal Information** includes information that cannot be used to +personally identify you, such as anonymous usage data, general +demographic information we may collect, referring/exit pages and URLs, +platform types, preferences you submit and preferences that are +generated based on the data you submit and number of clicks. **Personal +Information** includes your email and name which you submit to us through the +registration process at the Site. + +1.1. _Information collected via Technology_ + +To activate the Service you do not need to submit any Personal +Information other than your email address and name. To use the Service +thereafter, you do not need to submit further Personal +Information. +However, in an effort to improve the quality of the Service, we track +information provided to us by your browser or by our software +application when you view or use the Service, such as the website you +came from (known as the "referring URL"), the type of browser you use, +the device from which you connected to the Service, the time and date of +access, and other information that does not personally identify you. We +track this information using cookies, or small text files which include +an anonymous unique identifier. Cookies are sent to a user's browser +from our servers and are stored on the user's computer hard drive. +Sending a cookie to a user's browser enables us to collect Non-Personal +information about that user and keep a record of the user's preferences +when utilizing our services, both on an individual and aggregate basis. +For example, the Company may use cookies to collect the following +information: + +- how often you use our websites and services +- which content and features you use + +The Company may use both persistent and session cookies; persistent +cookies remain on your computer after you close your session and until +you delete them, while session cookies expire when you close your +browser. + +1.2. _Information you provide us by registering for an account_ + +In addition to the information provided automatically by your browser +when you visit the Site, to become a subscriber to the Service you will +need to create a personal profile. You can create a profile by +registering with the Service and entering your email address, and +creating a user name and a password. By registering, you are authorizing +us to collect, store and use your email address in accordance with this +Privacy Policy. + +1.3. _Children's Privacy_ + +The Site and the Service are not directed to anyone under the age of 13. +The Site does not knowingly collect or solicit information from anyone +under the age of 13, or allow anyone under the age of 13 to sign up for +the Service. In the event that we learn that we have gathered personal +information from anyone under the age of 13 without the consent of a +parent or guardian, we will delete that information as soon as possible. +If you believe we have collected such information, please contact us at +info@getzep.com. + +2. HOW WE USE AND SHARE INFORMATION + +_Personal Information:_ + +Except as otherwise stated in this Privacy Policy, we do not sell, +trade, rent or otherwise share for marketing purposes your Personal +Information with third parties without your consent. We do share +Personal Information with vendors who are performing services for the +Company, such as the servers for our email communications who are +provided access to user's email address for purposes of sending emails +from us. Those vendors use your Personal Information only at our +direction and in accordance with our Privacy Policy. + +In general, the Personal Information you provide to us is used to help +us communicate with you. For example, we use Personal Information to +contact users in response to questions, solicit feedback from users, +provide technical support, and inform users about promotional offers. + +We may share Personal Information with outside parties if we have a +good-faith belief that access, use, preservation or disclosure of the +information is reasonably necessary to meet any applicable legal process +or enforceable governmental request; to enforce applicable Terms of +Service, including investigation of potential violations; address fraud, +security or technical concerns; or to protect against harm to the +rights, property, or safety of our users or the public as required or +permitted by law. + +_Non-Personal Information_ + +In general, we use Non-Personal Information to help us improve the +Service and customize the user experience. We also aggregate +Non-Personal Information in order to track trends and analyze use +patterns on the Site. This Privacy Policy does not limit in any way our +use or disclosure of Non-Personal Information and we reserve the right +to use and disclose such Non-Personal Information to our partners, +advertisers and other third parties at our discretion. + +In the event we undergo a business transaction such as a merger, +acquisition by another company, or sale of all or a portion of our +assets, your Personal Information may be among the assets transferred. +You acknowledge and consent that such transfers may occur and are +permitted by this Privacy Policy, and that any acquirer of our assets +may continue to process your Personal Information as set forth in this +Privacy Policy. If our information practices change at any time in the +future, we will post the policy changes to the Site so that you may opt +out of the new information practices. We suggest that you check the Site +periodically if you are concerned about how your information is used. + +3. HOW WE PROTECT INFORMATION + +We implement security measures designed to protect your information from +unauthorized access. Your account is protected by your account password +and we urge you to take steps to keep your personal information safe by +not disclosing your password and by logging out of your account after +each use. We further protect your information from potential security +breaches by implementing certain technological security measures +including encryption, firewalls and secure socket layer technology. +However, these measures do not guarantee that your information will not +be accessed, disclosed, altered or destroyed by breach of such firewalls +and secure server software. By using our Service, you acknowledge that +you understand and agree to assume these risks. + +4. YOUR RIGHTS REGARDING THE USE OF YOUR PERSONAL + INFORMATION + +You have the right at any time to prevent us from contacting you for +marketing purposes.  When we send a promotional communication to a user, +the user can opt out of further promotional communications by following +the unsubscribe instructions provided in each promotional e-mail. +Please note that notwithstanding the +promotional preferences you indicate by either unsubscribing, we may continue to +send you administrative emails including, for example, periodic updates +to our Privacy Policy. + +5. LINKS TO OTHER WEBSITES + +As part of the Service, we may provide links to or compatibility with +other websites or applications. However, we are not responsible for the +privacy practices employed by those websites or the information or +content they contain. This Privacy Policy applies solely to information +collected by us through the Site and the Service. Therefore, this +Privacy Policy does not apply to your use of a third party website +accessed by selecting a link on our Site or via our Service. To the +extent that you access or use the Service through or on another website +or application, then the privacy policy of that other website or +application will apply to your access or use of that site or +application. We encourage our users to read the privacy statements of +other websites before proceeding to use them. + +6. CHANGES TO OUR PRIVACY POLICY + +The Company reserves the right to change this policy and our Terms of +Service at any time.  We will notify you of significant changes to our +Privacy Policy by sending a notice to the primary email address +specified in your account or by placing a prominent notice on our site. +Significant changes will go into effect 30 days following such +notification. Non-material changes or clarifications will take effect +immediately. You should periodically check the Site and this privacy +page for updates. + +7. CONTACT US + +If you have any questions regarding this Privacy Policy or the practices +of this Site, please contact us by sending an email to info@getzep.com. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/terms-of-service.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/terms-of-service.mdx new file mode 100644 index 00000000000..ab27159eec1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/terms-of-service.mdx @@ -0,0 +1,110 @@ +--- +title: "Terms of Service" +--- + +**Version 1.0** + +**Last revised on: January 27^th^, 2024** + +If you signed a separate Cover Page to access the Product with the same +account, and that agreement has not ended, the terms below do not apply +to you. Instead, your separate Cover Page applies to your use of the +Product. + +This Agreement is between Zep Software, Inc. and the company or person +accessing or using the Product. This Agreement consists of: (1) the +Order Form and (2) the Key Terms, both of which are on the Cover Page +below, and (3) the Common Paper [Cloud Service Agreement Standard Terms +Version 1.1](https://commonpaper.com/standards/cloud-service-agreement/1.1/) +("Standard Terms"). Any modifications to the Standard Terms made in the +Cover Page will control over conflicts with the Standard Terms. +Capitalized words have the meanings or descriptions given in the Cover +Page or the Standard Terms. + +If you are accessing or using the Product on behalf of your company, you represent that you +are authorized to accept this Agreement ßon behalf of your company. By +signing up, accessing, or using the Product, Customer indicates its +acceptance of this Agreement and agrees to be bound by the terms and +conditions of this Agreement. + +Cover Page + +_Order Form_ + +**Cloud Service:** Zep is a cloud-based platform-as-a-service that +offers fast, scalable, privacy-compliant building blocks for Generative +AI apps. + +**Subscription Start Date:** The Effective Date + +**Subscription Period:** 1 month(s) + +**Non-Renewal Notice Period:** At least 30 days before the end of the +current Subscription Period. + +**Cloud Service Fees:** + +Section 5.2 of the Standard Terms is replaced with: Certain parts of the +Product have different pricing plans, which are available at Provider's +[pricing page](/service-plans). Within the Payment +Period, Customer will pay Provider fees based on the Product tier +selected at the time of account creation and Customer's usage per +Subscription Period. Provider may update Product pricing by giving at +least 30 days notice to Customer (including by email or notification +within the Product), and the change will apply in the next Subscription +Period. + +**Payment Period:** 5 day(s) from the last day of the Subscription +Period + +**Invoice Period:** Monthly + +_Key Terms_ + +**Customer:** The company or person who accesses or uses the Product. If +the person accepting this Agreement is doing so on behalf of a company, +all use of the word \"Customer\" in the Agreement will mean that +company. + +**Provider:** Zep Software, Inc. + +**Effective Date:** The date Customer first accepts this Agreement. + +**Covered Claims:** + +**Provider Covered Claims:** Any action, proceeding, or claim that the +Cloud Service, when used by Customer according to the terms of the +Agreement, violates, misappropriates, or otherwise infringes upon anyone +else's intellectual property or other proprietary rights. + +**Customer Covered Claims:** Any action, proceeding, or claim that (1) +the Customer Content, when used according to the terms of the Agreement, +violates, misappropriates, or otherwise infringes upon anyone else's +intellectual property or other proprietary rights; or (2) results from +Customer's breach or alleged breach of Section 2.1 (Restrictions on +Customer). + +**General Cap Amount:** + +The fees paid or payable by Customer to provider in the 12 month period +immediately before the claim + +**Governing Law:** The laws of the State of Delaware + +**Chosen Courts:** The state or federal courts located in Delaware + +**Notice Address:** + +For Provider: notices@getzep.com + +For Customer: The main email address on Customer's account + +_Changes to the Standard Terms_ + +**Publicity Rights:** Modifying Section 14.7 of the Standard Terms, +Provider may identify Customer and use Customer's logo and trademarks on +Provider's website and in marketing materials to identify Customer as a +user of the Product. Customer hereby grants Provider a non-exclusive, +royalty-free license to do so in connection with any marketing, +promotion, or advertising of Provider or the Product during the length +of the Agreement. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/website-terms-of-use.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/website-terms-of-use.mdx new file mode 100644 index 00000000000..47e82a4b022 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/legal/website-terms-of-use.mdx @@ -0,0 +1,646 @@ +--- +title: "Website Terms of Use" +--- + +**Version 1.0** + +**Last revised on: January 27^th^, 2024** + +The website located at getzep.com (the "**Site**") is a copyrighted work +belonging to Zep Software, Inc. ("**Company**", "**us**", "**our**", and +"**we**"). Certain features of the Site may be subject to additional +guidelines, terms, or rules, which will be posted on the Site in +connection with such features. All such additional terms, guidelines, +and rules are incorporated by reference into these Terms. + +These Terms of Use (these "**Terms**") set forth the legally binding +terms and conditions that govern your use of the Site. By accessing or +using the Site, you are accepting these Terms (on behalf of yourself or +the entity that you represent), and you represent and warrant that you +have the right, authority, and capacity to enter into these Terms (on +behalf of yourself or the entity that you represent). you may not access +or use the Site or accept the Terms if you are not at least 18 years +old. If you do not agree with all of the provisions of these Terms, do +not access and/or use the Site. + +**PLEASE BE AWARE THAT SECTION 8.2 CONTAINS PROVISIONS GOVERNING HOW TO +RESOLVE DISPUTES BETWEEN YOU AND COMPANY. AMONG OTHER THINGS, SECTION +8.2 INCLUDES AN AGREEMENT TO ARBITRATE WHICH REQUIRES, WITH LIMITED +EXCEPTIONS, THAT ALL DISPUTES BETWEEN YOU AND US SHALL BE RESOLVED BY +BINDING AND FINAL ARBITRATION. SECTION 8.2 ALSO CONTAINS A CLASS ACTION +AND JURY TRIAL WAIVER. PLEASE READ SECTION 8.2 CAREFULLY.** + +**UNLESS YOU OPT OUT OF THE AGREEMENT TO ARBITRATE WITHIN 30 DAYS: (1) +YOU WILL ONLY BE PERMITTED TO PURSUE DISPUTES OR CLAIMS AND SEEK RELIEF +AGAINST US ON AN INDIVIDUAL BASIS, NOT AS A PLAINTIFF OR CLASS MEMBER IN +ANY CLASS OR REPRESENTATIVE ACTION OR PROCEEDING AND YOU WAIVE YOUR +RIGHT TO PARTICIPATE IN A CLASS ACTION LAWSUIT OR CLASS-WIDE +ARBITRATION; AND (2) YOU ARE WAIVING YOUR RIGHT TO PURSUE DISPUTES OR +CLAIMS AND SEEK RELIEF IN A COURT OF LAW AND TO HAVE A JURY TRIAL.** + +1. **Accounts** + +1.1. **Account Creation.** In order to use certain features of the Site, +you must register for an account ("**Account**") and provide certain +information about yourself as prompted by the account registration +form. You represent and warrant that: (a) all required registration +information you submit is truthful and accurate; (b) you will +maintain the accuracy of such information. You may delete your +Account at any time, for any reason, by following the instructions +on the Site. Company may suspend or terminate your Account in +accordance with Section 7. + +1.2. **Account Responsibilities.** You are responsible for maintaining +the confidentiality of your Account login information and are fully +responsible for all activities that occur under your Account. You +agree to immediately notify Company of any unauthorized use, or +suspected unauthorized use of your Account or any other breach of +security. Company cannot and will not be liable for any loss or +damage arising from your failure to comply with the above +requirements. + +2. **Access to the Site** + +2.1. **License.** Subject to these Terms, Company grants you a +non-transferable, non-exclusive, revocable, limited license to use +and access the Site solely for your own personal, noncommercial use. + +2.2. **Certain Restrictions.** The rights granted to you in these Terms +are subject to the following restrictions: (a) you shall not +license, sell, rent, lease, transfer, assign, distribute, host, or +otherwise commercially exploit the Site, whether in whole or in +part, or any content displayed on the Site; (b) you shall not +modify, make derivative works of, disassemble, reverse compile or +reverse engineer any part of the Site; (c) you shall not access the +Site in order to build a similar or competitive website, product, or +service; and (d) except as expressly stated herein, no part of the +Site may be copied, reproduced, distributed, republished, +downloaded, displayed, posted or transmitted in any form or by any +means. Unless otherwise indicated, any future release, update, or +other addition to functionality of the Site shall be subject to +these Terms. All copyright and other proprietary notices on the Site +(or on any content displayed on the Site) must be retained on all +copies thereof. + +2.3. **Modification.** Company reserves the right, at any time, to +modify, suspend, or discontinue the Site (in whole or in part) with +or without notice to you. You agree that Company will not be liable +to you or to any third party for any modification, suspension, or +discontinuation of the Site or any part thereof. + +2.4. **No Support or Maintenance.** You acknowledge and agree that +Company will have no obligation to provide you with any support or +maintenance in connection with the Site. + +2.5. **Ownership.** You acknowledge that all the intellectual property +rights, including copyrights, patents, trade marks, and trade +secrets, in the Site and its content are owned by Company or +Company's suppliers. Neither these Terms (nor your access to the +Site) transfers to you or any third party any rights, title or +interest in or to such intellectual property rights, except for the +limited access rights expressly set forth in Section 2.1. Company +and its suppliers reserve all rights not granted in these Terms. +There are no implied licenses granted under these Terms. + +2.6. **Feedback.** If you provide Company with any feedback or +suggestions regarding the Site ("**Feedback**"), you hereby assign +to Company all rights in such Feedback and agree that Company shall +have the right to use and fully exploit such Feedback and related +information in any manner it deems appropriate. Company will treat +any Feedback you provide to Company as non-confidential and +non-proprietary. You agree that you will not submit to Company any +information or ideas that you consider to be confidential or +proprietary. + +3. **Indemnification.** You agree to indemnify and hold + Company (and its officers, employees, and agents) harmless, + including costs and attorneys' fees, from any claim or demand made + by any third party due to or arising out of (a) your use of the + Site, (b) your violation of these Terms or (c) your violation of + applicable laws or regulations. Company reserves the right, at your + expense, to assume the exclusive defense and control of any matter + for which you are required to indemnify us, and you agree to + cooperate with our defense of these claims. You agree not to settle + any matter without the prior written consent of Company. Company + will use reasonable efforts to notify you of any such claim, action + or proceeding upon becoming aware of it. + +4. **Third-Party Links & Ads; Other Users** + +4.1. **Third-Party Links & Ads.** The Site may contain links to +third-party websites and services, and/or display advertisements for +third parties (collectively, "**Third-Party Links & Ads**"). Such +Third-Party Links & Ads are not under the control of Company, and +Company is not responsible for any Third-Party Links & Ads. Company +provides access to these Third-Party Links & Ads only as a +convenience to you, and does not review, approve, monitor, endorse, +warrant, or make any representations with respect to Third-Party +Links & Ads. You use all Third-Party Links & Ads at your own risk, +and should apply a suitable level of caution and discretion in doing +so. When you click on any of the Third-Party Links & Ads, the +applicable third party's terms and policies apply, including the +third party's privacy and data gathering practices. You should make +whatever investigation you feel necessary or appropriate before +proceeding with any transaction in connection with such Third-Party +Links & Ads. + +4.2. **Other Users.** Your interactions with other Site users are solely +between you and such users. You agree that Company will not be +responsible for any loss or damage incurred as the result of any +such interactions. If there is a dispute between you and any Site +user, we are under no obligation to become involved. + +4.3.. **Release.** You hereby release and forever discharge Company (and +our officers, employees, agents, successors, and assigns) from, and +hereby waive and relinquish, each and every past, present and future +dispute, claim, controversy, demand, right, obligation, liability, +action and cause of action of every kind and nature (including +personal injuries, death, and property damage), that has arisen or +arises directly or indirectly out of, or that relates directly or +indirectly to, the Site (including any interactions with, or act or +omission of, other Site users or any Third-Party Links & Ads). IF +YOU ARE A CALIFORNIA RESIDENT, YOU HEREBY WAIVE CALIFORNIA CIVIL +CODE SECTION 1542 IN CONNECTION WITH THE FOREGOING, WHICH STATES: "A +GENERAL RELEASE DOES NOT EXTEND TO CLAIMS WHICH THE CREDITOR OR +RELEASING PARTY DOES NOT KNOW OR SUSPECT TO EXIST IN HIS OR HER +FAVOR AT THE TIME OF EXECUTING THE RELEASE, WHICH IF KNOWN BY HIM OR +HER MUST HAVE MATERIALLY AFFECTED HIS OR HER SETTLEMENT WITH THE +DEBTOR OR RELEASED PARTY." + +5. **Disclaimers** + +THE SITE IS PROVIDED ON AN "AS-IS" AND "AS AVAILABLE" BASIS, AND +COMPANY (AND OUR SUPPLIERS) EXPRESSLY DISCLAIM ANY AND ALL WARRANTIES +AND CONDITIONS OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, +INCLUDING ALL WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, TITLE, QUIET ENJOYMENT, ACCURACY, OR +NON-INFRINGEMENT. WE (AND OUR SUPPLIERS) MAKE NO WARRANTY THAT THE SITE +WILL MEET YOUR REQUIREMENTS, WILL BE AVAILABLE ON AN UNINTERRUPTED, +TIMELY, SECURE, OR ERROR-FREE BASIS, OR WILL BE ACCURATE, RELIABLE, FREE +OF VIRUSES OR OTHER HARMFUL CODE, COMPLETE, LEGAL, OR SAFE. IF +APPLICABLE LAW REQUIRES ANY WARRANTIES WITH RESPECT TO THE SITE, ALL +SUCH WARRANTIES ARE LIMITED IN DURATION TO 90 DAYS FROM THE DATE OF +FIRST USE. + +SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO +THE ABOVE EXCLUSION MAY NOT APPLY TO YOU. SOME JURISDICTIONS DO NOT +ALLOW LIMITATIONS ON HOW LONG AN IMPLIED WARRANTY LASTS, SO THE ABOVE +LIMITATION MAY NOT APPLY TO YOU. + +6. **Limitation on Liability** + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL COMPANY (OR +OUR SUPPLIERS) BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY LOST PROFITS, +LOST DATA, COSTS OF PROCUREMENT OF SUBSTITUTE PRODUCTS, OR ANY INDIRECT, +CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, SPECIAL OR PUNITIVE DAMAGES +ARISING FROM OR RELATING TO THESE TERMS OR YOUR USE OF, OR INABILITY TO +USE, THE SITE, EVEN IF COMPANY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. ACCESS TO, AND USE OF, THE SITE IS AT YOUR OWN DISCRETION +AND RISK, AND YOU WILL BE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR +DEVICE OR COMPUTER SYSTEM, OR LOSS OF DATA RESULTING THEREFROM. + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, NOTWITHSTANDING ANYTHING TO THE +CONTRARY CONTAINED HEREIN, OUR LIABILITY TO YOU FOR ANY DAMAGES ARISING +FROM OR RELATED TO THESE TERMS (FOR ANY CAUSE WHATSOEVER AND REGARDLESS +OF THE FORM OF THE ACTION), WILL AT ALL TIMES BE LIMITED TO A MAXIMUM OF +FIFTY US DOLLARS. THE EXISTENCE OF MORE THAN ONE CLAIM WILL NOT ENLARGE +THIS LIMIT. YOU AGREE THAT OUR SUPPLIERS WILL HAVE NO LIABILITY OF ANY +KIND ARISING FROM OR RELATING TO THESE TERMS. + +SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OR EXCLUSION OF LIABILITY +FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE LIMITATION OR +EXCLUSION MAY NOT APPLY TO YOU. + +7. **Term and Termination.** Subject to this Section, + these Terms will remain in full force and effect while you use the + Site. We may suspend or terminate your rights to use the Site + (including your Account) at any time for any reason at our sole + discretion, including for any use of the Site in violation of these + Terms. Upon termination of your rights under these Terms, your + Account and right to access and use the Site will terminate + immediately. Company will not have any liability whatsoever to you + for any termination of your rights under these Terms, including for + termination of your Account. Even after your rights under these + Terms are terminated, the following provisions of these Terms will + remain in effect: Sections 2.2 through 2.6 and Sections 3 through 8. + +8. **General** + +8.1. **Changes.** These Terms are subject to occasional revision, and if +we make any substantial changes, we may notify you by sending you an +e-mail to the last e-mail address you provided to us (if any), +and/or by prominently posting notice of the changes on our Site. You +are responsible for providing us with your most current e-mail +address. In the event that the last e-mail address that you have +provided us is not valid, or for any reason is not capable of +delivering to you the notice described above, our dispatch of the +e-mail containing such notice will nonetheless constitute effective +notice of the changes described in the notice. Continued use of our +Site following notice of such changes shall indicate your +acknowledgement of such changes and agreement to be bound by the +terms and conditions of such changes. + +8.2. **Dispute Resolution.** Please read the following +arbitration agreement in this Section (the "**Arbitration +Agreement**") carefully.  It requires you to arbitrate disputes with +Company, its parent companies, subsidiaries, affiliates, successors +and assigns and all of their respective officers, directors, +employees, agents, and representatives (collectively, the "**Company +Parties**") and limits the manner in which you can seek relief from +the Company Parties + +(a) **Applicability of Arbitration Agreement** You +agree that any dispute between you and any of the Company Parties +relating in any way to the Site, the services offered on the Site +(the "**Services**") or these Terms will be resolved by binding +arbitration, rather than in court, except that (1) you and the +Company Parties may assert individualized claims in small claims +court if the claims qualify, remain in such court and advance solely +on an individual, non-class basis; and (2) you or the Company +Parties may seek equitable relief in court for infringement or other +misuse of intellectual property rights (such as trademarks, trade +dress, domain names, trade secrets, copyrights, and patents). **This +Arbitration Agreement shall survive the expiration or termination of +these Terms and shall apply, without limitation, to all claims that +arose or were asserted before you agreed to these Terms (in +accordance with the preamble) or any prior version of these +Terms.** This Arbitration Agreement does not preclude you from +bringing issues to the attention of federal, state or local +agencies. Such agencies can, if the law allows, seek relief against +the Company Parties on your behalf. For purposes of this Arbitration +Agreement, "**Dispute**" will also include disputes that arose or +involve facts occurring before the existence of this or any prior +versions of the Agreement as well as claims that may arise after the +termination of these Terms. + +(b) **Informal Dispute Resolution.** There might be instances when a +Dispute arises between you and Company. If that occurs, Company is +committed to working with you to reach a reasonable resolution. You +and Company agree that good faith informal efforts to resolve +Disputes can result in a prompt, low‐cost and mutually beneficial +outcome. You and Company therefore agree that before either party +commences arbitration against the other (or initiates an action in +small claims court if a party so elects), we will personally meet +and confer telephonically or via videoconference, in a good faith +effort to resolve informally any Dispute covered by this Arbitration +Agreement ("**Informal Dispute Resolution Conference**"). If you are +represented by counsel, your counsel may participate in the +conference, but you will also participate in the conference. + +The party initiating a Dispute must give notice to the other party +in writing of its intent to initiate an Informal Dispute Resolution +Conference ("**Notice**"), which shall occur within 45 days after +the other party receives such Notice, unless an extension is +mutually agreed upon by the parties. Notice to Company that you +intend to initiate an Informal Dispute Resolution Conference should +be sent by email to: info@getzep.com, or by regular mail to 2261 Market Street #5686 +San Francisco, CA 94114. The Notice must include: (1) +your name, telephone number, mailing address, e‐mail address +associated with your account (if you have one); (2) the name, +telephone number, mailing address and e‐mail address of your +counsel, if any; and (3) a description of your Dispute. + +The Informal Dispute Resolution Conference shall be individualized +such that a separate conference must be held each time either party +initiates a Dispute, even if the same law firm or group of law firms +represents multiple users in similar cases, unless all parties +agree; multiple individuals initiating a Dispute cannot participate +in the same Informal Dispute Resolution Conference unless all +parties agree. In the time between a party receiving the Notice and +the Informal Dispute Resolution Conference, nothing in this +Arbitration Agreement shall prohibit the parties from engaging in +informal communications to resolve the initiating party's Dispute. +Engaging in the Informal Dispute Resolution Conference is a +condition precedent and requirement that must be fulfilled before +commencing arbitration. The statute of limitations and any filing +fee deadlines shall be tolled while the parties engage in the +Informal Dispute Resolution Conference process required by this +section. + +(c) **Arbitration Rules and Forum.** These Terms evidence +a transaction involving interstate commerce; and notwithstanding any +other provision herein with respect to the applicable substantive +law, the Federal Arbitration Act, 9 U.S.C. § 1 et seq., will govern +the interpretation and enforcement of this Arbitration Agreement and +any arbitration proceedings. If the Informal Dispute Resolution +Process described above does not resolve satisfactorily within 60 +days after receipt of your Notice, you and Company agree that either +party shall have the right to finally resolve the Dispute through +binding arbitration. The Federal Arbitration Act governs the +interpretation and enforcement of this Arbitration Agreement. The +arbitration will be conducted by JAMS, an established alternative +dispute resolution provider. Disputes involving claims and +counterclaims with an amount in controversy under \$250,000, not +inclusive of attorneys' fees and interest, shall be subject to JAMS' +most current version of the Streamlined Arbitration Rules and +procedures available +at http://www.jamsadr.com/rules-streamlined-arbitration/; all +other claims shall be subject to JAMS's most current version of the +Comprehensive Arbitration Rules and Procedures, available +at http://www.jamsadr.com/rules-comprehensive-arbitration/. JAMS's +rules are also available at www.jamsadr.com or by calling JAMS at +800-352-5267. A party who wishes to initiate arbitration must +provide the other party with a request for arbitration (the +"**Request**"). The Request must include: (1) the name, telephone +number, mailing address, e‐mail address of the party seeking +arbitration and the account username (if applicable) as well as the +email address associated with any applicable account; (2) a +statement of the legal claims being asserted and the factual bases +of those claims; (3) a description of the remedy sought and an +accurate, good‐faith calculation of the amount in controversy in +United States Dollars; (4) a statement certifying completion of the +Informal Dispute Resolution process as described above; and (5) +evidence that the requesting party has paid any necessary filing +fees in connection with such arbitration. + +If the party requesting arbitration is represented by counsel, the +Request shall also include counsel's name, telephone number, mailing +address, and email address. Such counsel must also sign the Request. +By signing the Request, counsel certifies to the best of counsel's +knowledge, information, and belief, formed after an inquiry +reasonable under the circumstances, that: (1) the Request is not +being presented for any improper purpose, such as to harass, cause +unnecessary delay, or needlessly increase the cost of dispute +resolution; (2) the claims, defenses and other legal contentions are +warranted by existing law or by a nonfrivolous argument for +extending, modifying, or reversing existing law or for establishing +new law; and (3) the factual and damages contentions have +evidentiary support or, if specifically so identified, will likely +have evidentiary support after a reasonable opportunity for further +investigation or discovery. + +Unless you and Company otherwise agree, or the Batch Arbitration +process discussed in Subsection 8.2(h) is triggered, the arbitration +will be conducted in the county where you reside. Subject to the +JAMS Rules, the arbitrator may direct a limited and reasonable +exchange of information between the parties, consistent with the +expedited nature of the arbitration. If the JAMS is not available to +arbitrate, the parties will select an alternative arbitral forum. +Your responsibility to pay any JAMS fees and costs will be solely as +set forth in the applicable JAMS Rules. + +You and Company agree that all materials and documents exchanged +during the arbitration proceedings shall be kept confidential and +shall not be shared with anyone except the parties' attorneys, +accountants, or business advisors, and then subject to the condition +that they agree to keep all materials and documents exchanged during +the arbitration proceedings confidential. + +(d) **Authority of Arbitrator.** The arbitrator shall have +exclusive authority to resolve all disputes subject to arbitration +hereunder including, without limitation, any dispute related to the +interpretation, applicability, enforceability or formation of this +Arbitration Agreement or any portion of the Arbitration Agreement, +except for the following: (1) all Disputes arising out of or +relating to the subsection entitled "Waiver of Class or Other +Non-Individualized Relief," including any claim that all or part of +the subsection entitled "Waiver of Class or Other Non-Individualized +Relief" is unenforceable, illegal, void or voidable, or that such +subsection entitled "Waiver of Class or Other Non-Individualized +Relief" has been breached, shall be decided by a court of competent +jurisdiction and not by an arbitrator; (2) except as expressly +contemplated in the subsection entitled "Batch Arbitration," all +Disputes about the payment of arbitration fees shall be decided only +by a court of competent jurisdiction and not by an arbitrator; (3) +all Disputes about whether either party has satisfied any condition +precedent to arbitration shall be decided only by a court of +competent jurisdiction and not by an arbitrator; and (4) all +Disputes about which version of the Arbitration Agreement applies +shall be decided only by a court of competent jurisdiction and not +by an arbitrator. The arbitration proceeding will not be +consolidated with any other matters or joined with any other cases +or parties, except as expressly provided in the subsection entitled +"Batch Arbitration." The arbitrator shall have the authority to +grant motions dispositive of all or part of any claim or dispute. +The arbitrator shall have the authority to award monetary damages +and to grant any non-monetary remedy or relief available to an +individual party under applicable law, the arbitral forum's rules, +and these Terms (including the Arbitration Agreement). The +arbitrator shall issue a written award and statement of decision +describing the essential findings and conclusions on which any award +(or decision not to render an award) is based, including the +calculation of any damages awarded. The arbitrator shall follow the +applicable law. The award of the arbitrator is final and binding +upon you and us. Judgment on the arbitration award may be entered in +any court having jurisdiction. + +(e) **Waiver of Jury Trial.** EXCEPT AS SPECIFIED in +section 8.2(a) YOU AND THE COMPANY PARTIES HEREBY WAIVE ANY +CONSTITUTIONAL AND STATUTORY RIGHTS TO SUE IN COURT AND HAVE A TRIAL +IN FRONT OF A JUDGE OR A JURY. You and the Company Parties are +instead electing that all covered claims and disputes shall be +resolved exclusively by arbitration under this Arbitration +Agreement, except as specified in Section 8.2(a) above. An +arbitrator can award on an individual basis the same damages and +relief as a court and must follow these Terms as a court would. +However, there is no judge or jury in arbitration, and court review +of an arbitration award is subject to very limited review. + +(f) **Waiver of Class or Other Non-Individualized +Relief.**  YOU AND COMPANY AGREE THAT, EXCEPT AS +SPECIFIED IN SUBSECTION 8.2(h) EACH OF US MAY BRING CLAIMS AGAINST +THE OTHER ONLY ON AN INDIVIDUAL BASIS AND NOT ON A CLASS, +REPRESENTATIVE, OR COLLECTIVE BASIS, AND THE PARTIES HEREBY WAIVE +ALL RIGHTS TO HAVE ANY DISPUTE BE BROUGHT, HEARD, ADMINISTERED, +RESOLVED, OR ARBITRATED ON A CLASS, COLLECTIVE, REPRESENTATIVE, OR +MASS ACTION BASIS. ONLY INDIVIDUAL RELIEF IS AVAILABLE, AND DISPUTES +OF MORE THAN ONE CUSTOMER OR USER CANNOT BE ARBITRATED OR +CONSOLIDATED WITH THOSE OF ANY OTHER CUSTOMER OR USER. Subject to +this Arbitration Agreement, the arbitrator may award declaratory or +injunctive relief only in favor of the individual party seeking +relief and only to the extent necessary to provide relief warranted +by the party's individual claim. Nothing in this paragraph is +intended to, nor shall it, affect the terms and conditions under the +Subsection 8.2(h) entitled "Batch Arbitration." Notwithstanding +anything to the contrary in this Arbitration Agreement, if a court +decides by means of a final decision, not subject to any further +appeal or recourse, that the limitations of this subsection, "Waiver +of Class or Other Non-Individualized Relief," are invalid or +unenforceable as to a particular claim or request for relief (such +as a request for public injunctive relief), you and Company agree +that that particular claim or request for relief (and only that +particular claim or request for relief) shall be severed from the +arbitration and may be litigated in the state or federal courts +located in the State of California. All other Disputes shall be +arbitrated or litigated in small claims court. This subsection does +not prevent you or Company from participating in a class-wide +settlement of claims. + +(g) **Attorneys' Fees and Costs.** The parties shall bear their own +attorneys' fees and costs in arbitration unless the arbitrator finds +that either the substance of the Dispute or the relief sought in the +Request was frivolous or was brought for an improper purpose (as +measured by the standards set forth in Federal Rule of Civil +Procedure 11(b)). If you or Company need to invoke the authority of +a court of competent jurisdiction to compel arbitration, then the +party that obtains an order compelling arbitration in such action +shall have the right to collect from the other party its reasonable +costs, necessary disbursements, and reasonable attorneys' fees +incurred in securing an order compelling arbitration. The prevailing +party in any court action relating to whether either party has +satisfied any condition precedent to arbitration, including the +Informal Dispute Resolution Process, is entitled to recover their +reasonable costs, necessary disbursements, and reasonable attorneys' +fees and costs. + +(h) **Batch Arbitration.** To increase the efficiency of administration +and resolution of arbitrations, you and Company agree that in the +event that there are 100 or more individual Requests of a +substantially similar nature filed against Company by or with the +assistance of the same law firm, group of law firms, or +organizations, within a 30 day period (or as soon as possible +thereafter), the JAMS shall (1) administer the arbitration demands +in batches of 100 Requests per batch (plus, to the extent there are +less than 100 Requests left over after the batching described above, +a final batch consisting of the remaining Requests); (2) appoint one +arbitrator for each batch; and (3) provide for the resolution of +each batch as a single consolidated arbitration with one set of +filing and administrative fees due per side per batch, one +procedural calendar, one hearing (if any) in a place to be +determined by the arbitrator, and one final award ("**Batch +Arbitration**"). + +All parties agree that Requests are of a "substantially similar +nature" if they arise out of or relate to the same event or factual +scenario and raise the same or similar legal issues and seek the +same or similar relief. To the extent the parties disagree on the +application of the Batch Arbitration process, the disagreeing party +shall advise the JAMS, and the JAMS shall appoint a sole standing +arbitrator to determine the applicability of the Batch Arbitration +process ("**Administrative Arbitrator**"). In an effort to expedite +resolution of any such dispute by the Administrative Arbitrator, the +parties agree the Administrative Arbitrator may set forth such +procedures as are necessary to resolve any disputes promptly. The +Administrative Arbitrator's fees shall be paid by Company. + +You and Company agree to cooperate in good faith with the JAMS to +implement the Batch Arbitration process including the payment of +single filing and administrative fees for batches of Requests, as +well as any steps to minimize the time and costs of arbitration, +which may include: (1) the appointment of a discovery special master +to assist the arbitrator in the resolution of discovery disputes; +and (2) the adoption of an expedited calendar of the arbitration +proceedings. + +This Batch Arbitration provision shall in no way be interpreted as +authorizing a class, collective and/or mass arbitration or action of +any kind, or arbitration involving joint or consolidated claims +under any circumstances, except as expressly set forth in this +provision. + +(i) **30-Day Right to Opt Out.**  You have the right to opt out of the +provisions of this Arbitration Agreement by sending a timely written +notice of your decision to opt out to the following address: +2261 Market Street #5686, San Francisco, CA 94114, or email to info@getzep.com, +within 30 days after first becoming subject to this Arbitration +Agreement. Your notice must include your name and address and a +clear statement that you want to opt out of this Arbitration +Agreement. If you opt out of this Arbitration Agreement, all other +parts of these Terms will continue to apply to you. Opting out of +this Arbitration Agreement has no effect on any other arbitration +agreements that you may currently have with us, or may enter into in +the future with us. + +(j) **Invalidity, Expiration.** Except as provided in the subsection +entitled "Waiver of Class or Other Non-Individualized Relief", if +any part or parts of this Arbitration Agreement are found under the +law to be invalid or unenforceable, then such specific part or parts +shall be of no force and effect and shall be severed and the +remainder of the Arbitration Agreement shall continue in full force +and effect. You further agree that any Dispute that you have with +Company as detailed in this Arbitration Agreement must be initiated +via arbitration within the applicable statute of limitation for that +claim or controversy, or it will be forever time barred. Likewise, +you agree that all applicable statutes of limitation will apply to +such arbitration in the same manner as those statutes of limitation +would apply in the applicable court of competent jurisdiction. + +(k)**Modification.** Notwithstanding any provision in +these Terms to the contrary, we agree that if Company makes any +future material change to this Arbitration Agreement, you may reject +that change within 30 days of such change becoming effective by +writing Company at the following address: 2261 Market Street #5686, +San Francisco, CA 94114, or email to info@getzep.com. Unless you reject the +change within 30 days of such change becoming effective by writing +to Company in accordance with the foregoing, your continued use of +the Site and/or Services, including the acceptance of products and +services offered on the Site following the posting of changes to +this Arbitration Agreement constitutes your acceptance of any such +changes. Changes to this Arbitration Agreement do not provide you +with a new opportunity to opt out of the Arbitration Agreement if +you have previously agreed to a version of these Terms and did not +validly opt out of arbitration. If you reject any change or update +to this Arbitration Agreement, and you were bound by an existing +agreement to arbitrate Disputes arising out of or relating in any +way to your access to or use of the Services or of the Site, any +communications you receive, any products sold or distributed through +the Site, the Services, or these Terms, the provisions of this +Arbitration Agreement as of the date you first accepted these Terms +(or accepted any subsequent changes to these Terms) remain in full +force and effect. Company will continue to honor any valid opt outs +of the Arbitration Agreement that you made to a prior version of +these Terms. + +8.3. **Export.** The Site may be subject to U.S. export control laws and +may be subject to export or import regulations in other countries. +You agree not to export, reexport, or transfer, directly or +indirectly, any U.S. technical data acquired from Company, or any +products utilizing such data, in violation of the United States +export laws or regulations. + +8.4. **Disclosures.** Company is located at the address in Section 8.8. +If you are a California resident, you may report complaints to the +Complaint Assistance Unit of the Division of Consumer Product of the +California Department of Consumer Affairs by contacting them in +writing at 400 R Street, Sacramento, CA 95814, or by telephone +at (800) 952-5210. + +9.5. **Electronic Communications.** The communications between you and +Company use electronic means, whether you use the Site or send us +emails, or whether Company posts notices on the Site or communicates +with you via email. For contractual purposes, you (a) consent to +receive communications from Company in an electronic form; and (b) +agree that all terms and conditions, agreements, notices, +disclosures, and other communications that Company provides to you +electronically satisfy any legal requirement that such +communications would satisfy if it were be in a hardcopy writing. +The foregoing does not affect your non-waivable rights. + +8.6. **Entire Terms.** These Terms constitute the entire agreement +between you and us regarding the use of the Site. Our failure to +exercise or enforce any right or provision of these Terms shall not +operate as a waiver of such right or provision. The section titles +in these Terms are for convenience only and have no legal or +contractual effect. The word "including" means "including without +limitation". If any provision of these Terms is, for any reason, +held to be invalid or unenforceable, the other provisions of these +Terms will be unimpaired and the invalid or unenforceable provision +will be deemed modified so that it is valid and enforceable to the +maximum extent permitted by law. Your relationship to Company is +that of an independent contractor, and neither party is an agent or +partner of the other. These Terms, and your rights and obligations +herein, may not be assigned, subcontracted, delegated, or otherwise +transferred by you without Company's prior written consent, and any +attempted assignment, subcontract, delegation, or transfer in +violation of the foregoing will be null and void. Company may freely +assign these Terms. The terms and conditions set forth in these +Terms shall be binding upon assignees. + +8.7. **Copyright/Trademark Information**. Copyright ©2024 Zep Software, Inc. All rights +reserved. All trademarks, logos and service marks ("**Marks**") +displayed on the Site are our property or the property of other +third parties. You are not permitted to use these Marks without our +prior written consent or the consent of such third party which may +own the Marks. + +**Contact Information:** + +Daniel Chalef + +Address: + +2261 Market Street + +#5686 + +San Francisco, CA 94114 diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/favicon.png b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/favicon.png new file mode 100644 index 00000000000..b79387e7a88 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/favicon.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/zep-name-logo-gradient.svg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/zep-name-logo-gradient.svg new file mode 100644 index 00000000000..f8f57af1c7e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/zep-name-logo-gradient.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/zep-name-logo-pink.svg b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/zep-name-logo-pink.svg new file mode 100644 index 00000000000..3c5614c3645 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/logo/zep-name-logo-pink.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/mint.json b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/mint.json new file mode 100644 index 00000000000..513c5aa3f4e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/mint.json @@ -0,0 +1,206 @@ +{ + "$schema": "https://mintlify.com/schema.json", + "name": "Zep Documentation", + "logo": { + "dark": "/logo/zep-name-logo-pink.svg", + "light": "/logo/zep-name-logo-gradient.svg" + }, + "favicon": "/logo/favicon.png", + "colors": { + "primary": "#D5479D", + "light": "#D5479D", + "dark": "#D5479D", + "anchors": { + "from": "#5b2da8", + "to": "#D5479D" + } + }, + "topbarLinks": [ + { + "name": "Support", + "url": "mailto:help@getzep.com" + } + ], + "topbarCtaButton": { + "name": "Dashboard", + "url": "https://app.getzep.com" + }, + "tabs": [ + { + "name": "API Reference", + "url": "api-reference" + } + ], + "anchors": [ + { + "name": "Blog", + "icon": "newspaper", + "url": "https://blog.getzep.com" + }, + { + "name": "Community", + "icon": "discord", + "url": "https://discord.gg/W8Kw6bsgXQ" + }, + { + "name": "Service Status", + "icon": "gear", + "url": "https://status.getzep.com/" + } + ], + "navigation": [ + { + "group": "Getting Started", + "pages": ["sdks", "projects", "users"] + }, + { + "group": "Chat History Memory", + "pages": [ + "chat-history-memory/overview", + "chat-history-memory/memories", + "chat-history-memory/sessions", + "chat-history-memory/messages", + "chat-history-memory/summaries", + "chat-history-memory/search" + ] + }, + { + "group": "Dialog Tools", + "pages": ["dialog-classification", "question-synthesis"] + }, + { + "group": "Working with Search", + "pages": ["working-with-search"] + }, + { + "group": "Document Collections", + "pages": ["document-collections"] + }, + { + "group": "Ecosystem", + "pages": [ + { + "group": "LangChain", + "pages": [ + "langchain/overview", + "langchain/messagehistory", + "langchain/vectorstore", + { + "group": "Examples", + "pages": [ + "langchain/examples/messagehistory-example", + "langchain/examples/vectorstore-example", + "langchain/examples/rag-message-history-example" + ] + } + ] + }, + "ecosystem/flowise", + "ecosystem/chainlit", + "ecosystem/llamaindex", + "ecosystem/langflow", + "ecosystem/n8n" + ] + }, + { + "group": "Legal", + "pages": ["legal/privacy-policy", "legal/terms-of-service", "legal/website-terms-of-use"] + }, + { + "group": "document", + "pages": [ + "api-reference/document/updates-a-document-in-a-documentcollection-by-uuid", + "api-reference/document/gets-a-document-from-a-documentcollection-by-uuid", + "api-reference/document/creates-multiple-documents-in-a-documentcollection", + "api-reference/document/batch-deletes-documents-from-a-documentcollection-by-uuid", + "api-reference/document/batch-gets-documents-from-a-documentcollection", + "api-reference/document/batch-updates-documents-in-a-documentcollection", + "api-reference/document/delete-document-from-a-documentcollection-by-uuid", + "api-reference/document/searches-documents-in-a-documentcollection" + ] + }, + { + "group": "collection", + "pages": [ + "api-reference/collection/gets-a-list-of-documentcollections", + "api-reference/collection/gets-a-documentcollection", + "api-reference/collection/creates-a-new-documentcollection", + "api-reference/collection/deletes-a-documentcollection", + "api-reference/collection/updates-a-documentcollection", + "api-reference/collection/creates-an-index-for-a-documentcollection" + ] + }, + { + "group": "session", + "pages": [ + "api-reference/session/returns-all-sessions", + "api-reference/session/add-a-session", + "api-reference/session/returns-a-session-by-id", + "api-reference/session/add-a-session-1", + "api-reference/session/classify-a-session", + "api-reference/session/returns-a-sessions-summaries-by-id" + ] + }, + { + "group": "memory", + "pages": [ + "api-reference/memory/returns-a-memory-latest-summary-and-list-of-messages-for-a-given-session", + "api-reference/memory/add-memory-messages-to-a-given-session", + "api-reference/memory/delete-memory-messages-for-a-given-session", + "api-reference/memory/synthesize-a-question-for-a-given-session" + ] + }, + { + "group": "messages", + "pages": [ + "api-reference/messages/retrieves-all-messages-for-a-specific-session", + "api-reference/messages/retrieves-a-specific-message", + "api-reference/messages/updates-the-metadata-of-a-specific-message" + ] + }, + { + "group": "search", + "pages": ["api-reference/search/search-memory-messages-for-a-given-session"] + }, + { + "group": "user", + "pages": [ + "api-reference/user/list-all-users", + "api-reference/user/add-a-user", + "api-reference/user/list-all-users-1", + "api-reference/user/returns-a-user-by-id", + "api-reference/user/delete-a-user", + "api-reference/user/update-a-user", + "api-reference/user/list-all-sessions-for-a-user" + ] + } + ], + "openapi": ["/api-reference/openapi.json"], + "footerSocials": { + "twitter": "https://twitter.com/zep_ai", + "github": "https://github.com/getzep", + "linkedin": "https://www.linkedin.com/company/zep-ai" + }, + "redirects": [ + { + "source": "/privacy-policy", + "destination": "/legal/privacy-policy" + }, + { + "source": "/website-terms-of-use", + "destination": "/legal/website-terms-of-use" + }, + { + "source": "/terms-of-service", + "destination": "/legal/terms-of-service" + } + ], + "analytics": { + "posthog": { + "apiKey": "phc_dFYKy1OKnu2iKIl1Vvm8F256hEuk7aBkkImriWLdnbQ" + }, + "gtm": { + "tagId": "GTM-NS32L7KR" + } + } +} diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/projects.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/projects.mdx new file mode 100644 index 00000000000..f4e2bbaa049 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/projects.mdx @@ -0,0 +1,39 @@ + + Projects encapsulate abstractions such as Users, Sessions, Memories, and Documents, and various settings. This allows + you to keep your data organized by service, environment (such as development or production), or any other criteria + that makes sense for your use case. + + +## Creating a Project + +When you sign up for Zep, your first project is automatically created. You'll be asked to configure a few project-specific settings (details below). If you need more projects, you can create them anytime through the [Zep Web App](https://app.getzep.com/projects/create). + + + Create a new project + + +### Project Essentials + +- Unique Project Name: Choose a unique name for your project. +- Description (Optional): Feel free to add a brief description of your project. +- Model Selection: Let us know which LLM you'll be using. We count tokens in Zep artifacts like Messages and Summaries to help you stay within your prompt token budget. Knowing your model choice ensures we use the correct tokenizer. + +> **You can modify your project settings later from the Dashboard.** + +## Message Window + +The Message Window, also known as the Memory Window, is a project setting that specifies how many messages are retrieved in a `GetMemory` call. + +It's set to a default of `6` messages and, while not adjustable during the initial project setup, can be easily changed later through the dashboard. + +## LLM Model Selection and Token Counting + +Select the LLM you'll be using for your Assistant. We count tokens in Zep artifacts like Messages and Summaries to help you stay within your prompt token budget. + +Knowing your model choice ensures we use the correct tokenizer. + +Available options are `GPT 3.5 or 4 family` and `Llama2 and related`. + +## API Keys + +API keys are specific to each project. You can create multiple keys for a single project. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/question-synthesis.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/question-synthesis.mdx new file mode 100644 index 00000000000..67d92cdeea9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/question-synthesis.mdx @@ -0,0 +1,56 @@ +--- +title: "Question Synthesis" +--- + +Users often respond to questions with single-word answers that don't offer much information for search. +For example, a user may respond to a question about their favorite book with "Dune" or a question about their +dietary restrictions with "dairy". + + + Without context, many user messages lack information necessary to successfully embed a search query, resulting in poor + or irrelevant search results. + + +Zep provides a low-latency question synthesis API that can be used to generate a question from the current conversation context, using +the most recent message to center the question. The resulting question can be used to search over document Collections or +chat history Messages and Summaries. + +Zep's [Perpetual Memory](/chat-history-memory) uses this question synthesis functionality internally to ensure +that Memories it returns are relevant and useful. + +While it's possible to synthesize a question using a general purpose LLM, this is often a slow and inaccurate exercise. +Zep's private, fine-tuned models are designed to return results in hundreds of milliseconds. + + + Want to see Question Synthesis in action? Take a look at [Zep's LangService VectorStore + example](https://github.com/getzep/zep-python/blob/main/examples/langchain-langserve/app/message_history_vector_store_chain.py). + + + + + + +```Python +question = zep.memory.synthesize_question(session_id) +``` + + + + +```Typescript +const question = await zepClient.memory.synthesizeQuestion(sessionId); +``` + + + + +```Text +assistant: Iceland can be expensive. Costs depend on factors like accommodations, activities, and dining preferences. However, you can expect to spend around $200-$300 per day, not including flights. +user: Is it easy to find vegetarian or vegan food in Iceland? +assistant: Yes, Reykjavik has several vegetarian and vegan-friendly restaurants. Do you have any dietary restrictions? +user: Yes, dairy. +``` + +```Text +Can the user eat dairy products? +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/sdks.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/sdks.mdx new file mode 100644 index 00000000000..204d84ed016 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/sdks.mdx @@ -0,0 +1,144 @@ +--- +title: "SDK Installation" +description: "Zep provides SDKs for Python and TypeScript." +--- + +### Python + +To install the Zep Python SDK with Zep Cloud support, you'll need to install a release candidate version. + + + + + +```Bash +pip install --pre zep-python +``` + + + + +```Bash +poetry add zep-python@^2.0.0rc +``` + + + + +### TypeScript + +To install the Zep JavaScript SDK with Zep Cloud support, please install a pre-release version tagged with `@next`. + + + + + +```Bash +npm install @getzep/zep-js@next +``` + + + + +```Bash +yarn add @getzep/zep-js@next +``` + + + + +```Bash +pnpm install @getzep/zep-js@next +``` + + + + +## Initialize Client + + + + +```python +import os +from zep_python import ZepClient + +API_KEY = os.environ.get('ZEP_API_KEY') + +zep = ZepClient(api_key=API_KEY) +``` + + + + +```typescript +import { ZepClient } from "@getzep/zep-js"; + +API_KEY = process.env.ZEP_API_KEY; + +const zep = await ZepClient.init(API_KEY); +``` + + + + + + +**API Keys are project-specific.** You can generate a new API key from [Project Settings](projects). + + + **The Python SDK Supports Async Use** + +All methods are available as both sync and async, with the async methods prefixed with `a`. + + For example, zep-python + +has both `zep_client.memory.add_memory` and `zep_client.memory.aadd_memory` methods. + + + +## LangChain + + + + +The pre-release version of the `zep-python` SDK includes `ZepChatMessageHistory` and `ZepVectorStore` classes. + +These are designed to work seamlessly with [LangChain's Python Expression Language](https://python.langchain.com/docs/expression_language/). + +> To integrate these classes into your application, ensure the `langchain_core` package is installed. For installation guidance, please consult the [LangChain documentation](https://python.langchain.com/docs/get_started/installation#langchain-core). + +Import the classes as shown below: + +```python +from zep_python.langchain import ZepChatMessageHistory, ZepVectorStore +``` + + + + +The pre-release version of the `zep-js` SDK includes `ZepChatMessageHistory` and `ZepVectorStore` classes + +These are designed to work seamlessly with [LangChain's JavaScript Expression Language](https://js.langchain.com/docs/expression_language/). + +To utilize these classes in your application, ensure that the `langchain` package is installed: + +```bash +npm install langchain +``` + +To import these classes, use the following syntax: + +```typescript +import { ZepChatMessageHistory, ZepVectorStore } from "@getzep/zep-js/langchain"; +``` + + + + + + + +## LlamaIndex + +Stay tuned! We are in the process of updating our LlamaIndex integration to be compatible with the latest Zep API. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/users.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/users.mdx new file mode 100644 index 00000000000..fd2467f03e6 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/users.mdx @@ -0,0 +1,229 @@ +A User represents an individual interacting with your application. Each User can have multiple Sessions associated +with them, allowing you to track and manage the interactions of a user over time. + +The unique identifier for each user is their `UserID`. This can be any string value - for example, it could be a +username, an email address, or a UUID. You can also store additional data related to the user in the `metadata` field. + +The User object and its associated Sessions provide a powerful way to manage and understand the behavior of +individuals using your application. By associating Sessions with Users, you can track the progression of +conversations and interactions over time, providing valuable context and history. + +In the following sections, you will learn how to manage Users and their associated Sessions. + + +**Users Enable Simple User Privacy Management** + +By associating a User with a Session, you can delete all Sessions and session artifacts associated with a User +with a single API call. + + + +## The User model + +You can associate rich business context with a User: + +- `user_id`: A unique identifier of the user that maps to your internal User ID. +- `email`: The user's email. +- `first_name`: The user's first name. +- `last_name`: The user's last name. +- `metadata`: Any additional data associated with the user. + +## Adding a User + +You can add a new user by providing the user details. + + + + + +```python +from zep_python import ZepClient +from zep_python.user import CreateUserRequest, UpdateUserRequest + +client = ZepClient(api_key=API_KEY) + +user_request = CreateUserRequest( + user_id=user_id, + email="user@example.com", + first_name="Jane", + last_name="Smith", + metadata={"foo": "bar"}, +) +new_user = client.user.add(user_request) +``` + + + + +```typescript +import { ZepClient, CreateUserRequest, UpdateUserRequest } from "@getzep/zep-js"; + +const client: ZepClient = await ZepClient.init(API_KEY); + +const user_request: CreateUserRequest = { + userId: user_id, + email: "user@example.com", + firstName: "Jane", + lastName: "Smith", + metadata: { foo: "bar" }, +}; +const new_user = await client.user.add(user_request); +``` + + + + +> Learn how to associate [Sessions with Users](chat-history-memory/sessions) + +## Getting a User + +You can retrieve a user by their ID. + + + + + +```python +user = client.user.get("user123") +``` + + + + +```typescript +const user = await client.user.get("user123"); +``` + + + + +## Updating a User + +You can update a user's details by providing the updated user details. + + + + + +```python +user_request = UpdateUserRequest( + user_id=user_id, + email="updated_user@example.com", + first_name="Jane", + last_name="Smith", + metadata={"foo": "updated_bar"}, +) + +updated_user = client.user.update(user_request) +``` + + + + +```typescript +const user_request: UpdateUserRequest = { + userId: user_id, + email: "updated_user@example.com", + firstName: "Jane", + lastName: "Smith", + metadata: { foo: "updated_bar" }, +}; + +const updated_user = await client.user.update(user_request); +``` + + + + +## Deleting a User + +You can delete a user by their ID. + + + + + +```python +client.user.delete("user123") +``` + + + + +```typescript +await client.user.delete("user123"); +``` + + + + +## Getting a User's Sessions + +You can retrieve all Sessions for a user by their ID. + + + + +```python +sessions = client.user.get_sessions("user123") +``` + + + + +```typescript +const sessions = await client.user.getSessions("user123"); +``` + + + + +## Listing Users + +You can list all users, with optional limit and cursor parameters for pagination. + + + + + +```python +# List the first 10 users +users = client.user.list(limit=10, cursor=0) +``` + + + + +```typescript +// List the first 10 users +const users = await client.user.list(10, 0); +``` + + + + +## Listing Users in Chunks + +You can retrieve users in chunks of a specified size. This is a generator function that yields each chunk of users as +they are retrieved. + + + + +```python +for users in client.user.list_chunked(chunkSize=100): + process(users) +``` + + + + +```typescript +for await (const chunk of client.user.listChunked(100)) { + // Process each chunk of users + await processChunk(chunk); +} +``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/working-with-search.mdx b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/working-with-search.mdx new file mode 100644 index 00000000000..e651a186db2 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/fixtures/zep/working-with-search.mdx @@ -0,0 +1,285 @@ +--- +title: "Constructing Search Queries" +--- + + + Zep's Collection and Memory search supports semantic similarity search and similarity search re-ranked by [Maximal + Marginal Relevance](#maximal-marginal-relevance-re-ranking). Both of these search types can be filtered by + JSONPath-based metadata filters. Memory search also supports querying by message or summary creation date. + + +## Simple, Text-based Semantic Queries + +The simplest form of search query is a text-based semantic simailrity query. No metadata filter is required, and the query is simply a string of text. +Zep will convert the query into an embedding and find semantically similar documents, chat history summaries, or chat messages. + +Below is an example search against a chat session using only search text. + + + + + +```python +from zep_python import ( + MemorySearchPayload, + ZepClient, +) +client = ZepClient(api_key=API_KEY) + +search_payload = MemorySearchPayload( + text=query, + search_scope="messages", + metadata={"where": {"jsonpath": '$[*] ? (@.bar == "foo")'}}, + ) + +search_results = await client.memory.search_memory(session_id, search_payload) +``` + + + + + +```typescript +import { ZepClient, MemorySearchPayload } from "@getzep/zep-js"; + +const zepClient = await ZepClient.init(process.env.ZEP_API_KEY); + +const searchPayload = new MemorySearchPayload({ + text: query, + search_scope: "messages", + metadata: { where: { jsonpath: '$[*] ? (@.bar == "foo")' } }, +}); + +const searchResults = await client.memory.searchMemory(sessionId, searchPayload); +``` + + + + +Read more about [chat message history search](chat-history-memory/search). + +## Maximal Marginal Relevance Re-Ranking + +Zep supports re-ranking search results using [Maximal Marginal Relevance (MMR)](#maximal-marginal-relevance-re-ranking) over both chat history memory and document collections. + +Maximal Marginal Relevance (MMR) helps balance relevance and diversity in results +returned during information retrieval, which is useful in vector similarity searches for Retrieval-Augmented Generation (RAG) type applications. + +A similarity search may return many highly ranked results that are very similar to each other. Since each subsequent result doesn't add much new information to a prompt, +adding these may not be very useful. + +MMR helps to reduce redundancy in the results by re-ranking the results to promote diversity, with very similar results +downranked in favor of different, but still relevant, results. + +### How Zep's MMR Re-Ranking Works + +When you run a search re-ranked by MMR, Zep retrieves double the number of results, K, you requested. The entire resultset is then reranked using MMR, and the top K results are returned. + +If you request fewer than 10 results, Zep will return 10 results, but still return to you the top K results you requested. + +Zep's MMR algorithm is SIMD-hardware accelerated on `amd64` archicture CPUs, ensuring that MMR adds little overhead to your search. + +

Constructing an MMR Re-Ranked Search Query

+ +#### MMR Search over Chat History + + + + + +```python +from zep_python import ( + MemorySearchPayload, + ZepClient, +) + +client = ZepClient(api_key=API_KEY) + +search_payload = MemorySearchPayload( + text=query, + search_scope="summary", # This could be messages or summary + search_type="mmr", + mmr_lambda=0.5, # tune diversity vs relevance +) + +search_results = client.memory.search_memory( + session_id, search_payload, limit=3 +) +``` + + + + + +```typescript +import { ZepClient, MemorySearchPayload } from "@getzep/zep-js"; + +const zepClient = await ZepClient.init(process.env.ZEP_API_KEY); + +const searchPayload = new MemorySearchPayload({ + text: query, + search_scope: "summary", // This could be messages or summary + search_type: "mmr", + mmrLambda: 0.5, // tune diversity vs relevance +}); + +const searchResults = await client.memory.searchMemory(sessionId, searchPayload, 3); +``` + + + + +#### MMR Search over Document Collections + + + + + +```python +# This assumes you've created a collection and added documents to it +docs = collection.search( + text=query, + search_type="mmr", + mmr_lambda=0.5, + limit=3, +) +``` + + + + + +```typescript +// This assumes you've created a collection and added documents to it +const docs = await collection.search( + { + text: query, + search_type: "mmr", + mmr_lambda: 0.5, + }, + 3, +); +``` + + + + +## Filtering using Metadata + +Zep supports filtering search queries by metadata. Metadata filters are [JSONPath queries](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html) augmented by a simple boolean logic overlay. + +JSONPath queries allow for building sophisticated filters that match on elements at any level of the JSON document. The boolean logic overlay allows for combining multiple JSONPath queries using `and` and `or` operators. + +### Useful resources for building and testing JSONPath queries + +- [JSONPath Syntax](https://goessner.net/articles/JsonPath/) +- [JSONPath Online Evaluator](https://jsonpath.com/) +- [JSONPath Expression Tester](https://jsonpath.curiousconcept.com/#). + +### Constructing a JSONPath Query Filter + +The simplest form of a metadata filter looks as follows: + +```json +{ + "where": { "jsonpath": "$[*] ? (@.foo == \"bar\")" } +} +``` + +If a metadata field `foo` is equal to the string `"bar"`, then the document or message will be returned in the search results. + +Executing the above query against a chat session looks as follows: + + + + +```python +search_payload = MemorySearchPayload( + text="Is Lauren Olamina a character in a book", + metadata={ + "where": { + "jsonpath": '$[*] ? (@.author == "Octavia Butler")' + } + } +) + +search_results = client.memory.search_memory(session_id, search_payload) +``` + + + + + +```typescript +const searchPayload = new MemorySearchPayload({ + text: "Is Lauren Olamina a character in a book", + metadata: { where: { jsonpath: '$[*] ? (@.author == "Octavia Butler")' } }, +}); + +const search_results = await client.memory.searchMemory(session_id, search_payload); +``` + + + + +### Combining multiple JSONPath filters using boolean logic + +Multiple JSONPath queries can be combined using boolean logic. The following example will return documents or messages where the `author` field is equal to `"Octavia Butler"` **and** the `title` field is equal to `"Parable of the Sower"`. + +```json +{ + "where": { + "and": [ + { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" }, + { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" } + ] + } +} +``` + +Similarly, the following example will return documents or messages where the `author` field is equal to `"Octavia Butler"` **or** the `title` field is equal to `"Parable of the Sower"`. + +```json +{ + "where": { + "or": [ + { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" }, + { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" } + ] + } +} +``` + +Filter logic can be combined to create arbitrarily complex filters. For example, the following filter will return documents or messages where: + +- the `author` field is equal to (`"Octavia Butler"` **and** the `title` field is equal to `"Parable of the Sower"`) +- **or** the `title` field is equal to `"Parable of the Talents"`. + +```json +{ + "where": { + "or": [ + { + "and": [ + { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" }, + { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" } + ] + }, + { "jsonpath": "$[*] ? (@.title == \"Parable of the Talents\")" } + ] + } +} +``` + +## Querying by Message Creation Date + +Memory search supports querying by message creation date. The following example will return documents or messages created between June 1, 2023 and June 31, 2023. + +Datetime strings must be in ISO 8601 format. + +```json +{ + "start_date": "2023-06-01", + "end_date": "2023-06-31" +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/migrateFromMintlify.test.ts b/packages/cli/docs-importers/mintlify/src/__test__/migrateFromMintlify.test.ts new file mode 100644 index 00000000000..17f1dd19e27 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/migrateFromMintlify.test.ts @@ -0,0 +1,56 @@ +import { AbsoluteFilePath, doesPathExist, join, RelativeFilePath } from "@fern-api/fs-utils"; +import path from "path"; +import { MintlifyImporter } from ".."; +import { mkdir, rmdir } from "fs/promises"; +import { createMockTaskContext } from "@fern-api/task-context"; +import { CONSOLE_LOGGER } from "@fern-api/logger"; +import { FernDocsBuilderImpl } from "@fern-api/docs-importer-commons"; +import { writeFile } from "fs/promises"; +import { FERN_DIRECTORY, PROJECT_CONFIG_FILENAME } from "@fern-api/configuration"; + +const FIXTURES_PATH = AbsoluteFilePath.of(path.join(__dirname, "fixtures")); +const OUTPUTS_PATH = AbsoluteFilePath.of(path.join(__dirname, "outputs")); + +const fixtures = ["bland", "layerfi", "zep"]; + +describe("add-generator-groups", () => { + for (const fixture of fixtures) { + it(`${fixture}`, async () => { + const fixturePath = join(FIXTURES_PATH, RelativeFilePath.of(fixture)); + const outputPath = join(OUTPUTS_PATH, RelativeFilePath.of(fixture)); + if (await doesPathExist(outputPath)) { + await rmdir(outputPath, { recursive: true }); + } + await mkdir(outputPath, { recursive: true }); + + const context = createMockTaskContext({ logger: CONSOLE_LOGGER }); + const mintlifyImporter = new MintlifyImporter({ + context + }); + const builder = new FernDocsBuilderImpl(); + + await mintlifyImporter.import({ + args: { absolutePathToMintJson: join(fixturePath, RelativeFilePath.of("mint.json")) }, + builder + }); + + await builder.build({ outputDirectory: outputPath }); + + await writeFile( + join( + AbsoluteFilePath.of(outputPath), + RelativeFilePath.of(FERN_DIRECTORY), + RelativeFilePath.of(PROJECT_CONFIG_FILENAME) + ), + JSON.stringify( + { + version: "*", + organization: "fern" + }, + undefined, + 4 + ) + ); + }); + } +}); diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch.mdx new file mode 100644 index 00000000000..882897adbe7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch.mdx @@ -0,0 +1,94 @@ +--- +title: Batch Calling +subtitle: Send a series of calls with a single api call +slug: api-reference/batch-endpoint/batch +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + This is the prompt or task used for all the phone calls in the request. Information can be inserted into it surrounding variable names with \{\{curly braces\}\}. + +Example: + +```json +"You are calling {{business}} to renew their subscription to {{service}} before it expires on {{date}}." +``` + + + + + Define a list of calls to make and their properties. + + Each call in call_data *must* have a `phone_number` property. Properties are case-sensitive. + +Example: + +```json +[ + { + "phone_number": "1234567890", + "business": "ABC co.", + "service": "Netflix", + "date": "September 4th" + }, + { + "phone_number": "32176540987", + "business": "XYZ inc.", + "service": "Window Cleaning", + "date": "December 20th" + } +] +``` + + + + + Adds a user-friendly label to your batch to keep track of it's original intention. This can help differentiate + multiple call batches that are part of the same Campaign. Shown when a batch is retreived. + + + + Use ```campaign_id``` to organize related batches together. This can be set manually or auto-generated through + Campaigns. + + + + When this is set to ```true```, only the first call of ```call_data``` will be dispatched. A common use case is to set the first ```phone_number``` value to your own to confirm everything's set up properly. + +Includes additional information in the response when true so that it's easier to find any issues. + + + + + All other parameters supported by the [Send Call](/api-v1/post/calls) endpoint are supported here as well. They will + be applied to each call in the batch. + + +### Response + + + If anything other than "success" is returned, there was an error. + + + + The unique identifier for the batch. + + + + +```json Response +{ + "message": "success", + "batch_id": "3p$7rQ3p9sT5bzmF-gen-batch" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch_get.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch_get.mdx new file mode 100644 index 00000000000..66bca0489b7 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch_get.mdx @@ -0,0 +1,267 @@ +--- +title: Retrieve a Batch +subtitle: Retrieves calls and batch data for a specific batch_id. +slug: api-reference/batch-endpoint/batch_get +--- + + +### Headers + + + Your API key for authentication. + + +### Query Parameters + + + The unique identifier for the batch of calls you want to retrieve. + + + + Whether or not to include individual call data in the response. + + + + If calls are included, can be set to false to exclude transcripts from the response. + + + + If calls are included, can be set to false to exclude analysis from the response. + + +### Response + + + An object containing parameters and settings for the batch. + + + + The unique identifier of the batch - used as the `batch_id` parameter in other API calls. + + + + The creation timestamp of the batch. + + + + The label or description of the batch. + + + + The base prompt used for calls in this batch. + + + + The endpoint code used for API integration. + + + + An object containing parameters for the calls in the batch. + + + + An object containing analysis data for the batch. + + + + The total number of calls in the batch, including completed and in-progress calls. + + + + The total number of completed calls in the batch. + + + + The total number of in-progress calls in the batch. + + + + An object containing the number of calls in each queue status. + +Example: + +```json +{ + "complete": 237, + "queued": 2, + "call_error": 1 +} +``` + + + + + Contains `average`, `average_nonzero`, `summary` and `all` fields. + +- `average`: The average call length in seconds. +- `average_nonzero`: The average call length in seconds, excluding calls with a length of less than a second. +- `summary`: A summary of the call lengths, grouped into ranges. +- `all`: Contains each call length, in case you want to use a different grouping than the default. + + + + Contains each `call_id` in the batch. + + + + Contains any error messages that calls in the batch may have. + +Example: + +```json +[ + { + "call_id": "c52f5f8c-147e-478c-4b40-88214feeba29", + "error_message": "Cannot transfer to +12223334444 - Call is no longer active" + } +] +``` + + + + + Contains the number of calls that have been sent to each endpoint. Applicable only to API integrations. + + + + An array of objects, each representing individual call data. + + + + The timestamp when the individual call was created. + + + + The phone number the call was made to. + + + + The phone number the call was made from. + + + + Indicates if the call was completed. + + + + The unique identifier for each individual call. + + + + The duration of the call in minutes. + + + + +```json Response +{ + "batch_params": { + "id": "AAcQq8zXxLB56LWg-gen-batch", + "campaign_id": null, + "created_at": "2023-12-07T20:43:44.544773+00:00", + "label": "Customer Satisfaction Follow-up", + "base_prompt": "You are calling {{name}} about their recent purchase of the item: {{item}}. Ask them each of the following questions about the specific item they purchased: {{questions}}", + "endpoint_code": "api", + "call_params": { + "reduce_latency": true, + "voice_id": 0, + "language": "eng", + "max_duration": 10, + "wait_for_greeting": false + } + }, + "analysis": { + "total_calls": 88, + "completed_calls": 86, + "in_progress_calls": 2, + "queue_statuses": { + "complete": 85, + "started": 2, + "call_error": 1 + }, + "call_lengths": { + "average": 17, + "average_nonzero": 31, + "summary": { + "0-5": 18, + "5-10": 4, + "10-15": 3, + "15-20": 2, + "20-30": 14, + "30-45": 28, + "45-60": 11, + "60-90": 6, + "90-120": 1, + "120+": 1 + }, + "all": [ + 7, 32 + //... + ] + }, + "call_ids": [ + "ffa99be3-63dd-44dc-9523-380cd25c1b9e", + "591338a8-34b2-41e6-93da-b9029c9bdedc" + //... + ], + "error_messages": [ + { + "call_id": "c52f5f8c-147e-478c-4b40-88214feeba29", + "error_message": "Cannot transfer to +12223334444 - Call is no longer active" + } + ], + "endpoints": { + "api.bland.ai": 88 + } + }, + "call_data": [ + { + "status": "completed", + "corrected_duration": "12", + "end_at": "2023-12-16T00:17:38.000Z", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e", + "to": "1112223333", + "from": "+17473423273", + "completed": true, + "created_at": "2023-12-16T00:17:22.383682+00:00", + "queue_status": "complete", + "endpoint_url": "api.bland.ai", + "max_duration": 30, + "error_message": null, + "request_data": { + "phone_number": "1112223333", + "reduce_latency": true, + "wait": false, + "language": "ENG" + }, + "transcripts": [ + { + "id": 1188954, + "created_at": "2023-12-16T00:17:30.46833+00:00", + "text": " Hi, Im calling about the laundromat for sale. —  ", + "user": "assistant", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + }, + { + "id": 1188957, + "created_at": "2023-12-16T00:17:35.14056+00:00", + "text": "I'll get back to you as soon as you can. Just leave a message. Thank you. Bye.", + "user": "user", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + }, + { + "id": 1188959, + "created_at": "2023-12-16T00:17:36.710551+00:00", + "text": "Ended call: Goodbye", + "user": "agent-action", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + } + ], + "call_length": 6.242 + } + //... + ] +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch_stop.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch_stop.mdx new file mode 100644 index 00000000000..142e7cc5c69 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batch_stop.mdx @@ -0,0 +1,50 @@ +--- +title: Cancel Batch +subtitle: Terminates every dispatched call for a specific batch. +slug: api-reference/batch-endpoint/batch_stop +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + The unique identifier for the batch to be cancelled. + + +### Response + + + The status of the request. If anything other than 'success', an error has occurred or all calls have already been + completed. + + + + A message describing the status of the request. + + + + The number of calls that were cancelled. + + + + The `batch_id` of the cancelled batch. + + + + +```json Response +{ + "status": "success", + "message": "Successfully stopped batch", + "num_calls": 1204, + "batch_id": "5e5b1a3a-2b0a-4b9e-8b9e-asdf-gen-batch" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batches_get.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batches_get.mdx new file mode 100644 index 00000000000..e537d84f36b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/batches_get.mdx @@ -0,0 +1,78 @@ +--- +title: Retrieve All Batches +subtitle: Retrieves batch-specific data for each batch you've created. +slug: api-reference/batch-endpoint/batches_get +--- + + +### Headers + + + Your API key for authentication. + + +### Response + + + Can be `success` or `error`. + + + + Contains an array of batch objects. + + + + The unique identifier for the batch. + + + + The original base prompt used to create the batch. Will still contain the original placeholder variables such as ` + {{ business }}` or `{{ name }}`. + + + + The label you assigned to the batch (if any). + + + + Enterprise customers with custom endpoints will see the endpoint code here if specified. + + + + The base call parameters used to create the batch, such as `voice_id`, `max_duration`, `reduce_latency`, and + `wait_for_greeting`. + + + + The date and time the batch was created. + + + +```json Response +{ + "status": "success", + "batches": [ + { + "batch_id": "ZfowpkhOSVCZJ94i-gen-batch", + "campaign_id": "a2shduf92f74p8288c93nid5", + "created_at": "2023-11-16T22:14:24.9663+00:00", + "label": "Subscription Renewal Reminders", + "base_prompt": "You are calling {{business}} and need to let them know that their subscription to {{service}} is going to expire on {{date}}. If they'd like to renew, take their credit card information and bill them through {{url}}", + "endpoint_code": "api", + "call_params": { + "reduce_latency": true, + "voice_id": 2, + "language": "eng", + "request_data": { + "test_param": "request data.test_param", + "your name": "Janessa" + }, + "max_duration": 5, + "wait_for_greeting": false + } + }, + //... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/end_batches.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/end_batches.mdx new file mode 100644 index 00000000000..17b24fea574 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/batch-endpoint/end_batches.mdx @@ -0,0 +1,48 @@ +--- +title: Cancel All Batch Calls +subtitle: Terminates every dispatched call in each running batch. +slug: api-reference/batch-endpoint/end_batches +--- + + +### Headers + + + Your API key for authentication. + + +### Response + + + The status of the request. If anything other than 'success', an error has occurred or all calls have already been + completed. + + + + A message describing the status of the request. + + + + The number of calls that were cancelled. + + + + An array of batch_ids that were cancelled. + + + + +```json Response +{ + "status": "success", + "message": "Successfully stopped batch", + "num_calls": 9704, + "batch_ids": [ + "5e5b1a3a-2b0a-4b9e-8b9e-e42a-gen-batch", + "b4eb1a3a-4b9e-230a-8b9e-acdc-gen-batch" + "92ab1a3a-a82e-339b-4b9e-8b9e-gen-batch" + ] +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/call.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/call.mdx new file mode 100644 index 00000000000..3a4cf5b5b8f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/call.mdx @@ -0,0 +1,271 @@ +--- +title: Make a call +subtitle: This endpoint sends an outbound AI phone call. +slug: api-reference/endpoint/call +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to call. Country code required for non-US numbers. + +Example: `+12223334444`, `+44770090000`, `+61491550156`. + + + + + A phone number that the agent can transfer to under specific conditions, such as when the caller/callee asks to speak to a human. + +For best results, specify the exact conditions to transfer under in the `task` parameter. Refer to the action as "transfer", any other phrasing such as "swap" or "switch" can cause unexpected behavior. + +Example: `+12223334444`, `+44770090000`, `+61491550156`. + + + + + Specify a purchased Outbound Number to call from. Country code is required, spaces or parentheses must be excluded. + +By default, calls are initiated from a separate pool of numbers owned by Bland. + + + + + Define how the AI should behave. Provide instructions, relevant information, and examples of the ideal conversation flow. + +Note: Concise prompts lead to higher performance and adherence to instructions. Overly verbose prompts 'dilute' the context if filled with unnecessary/irrelevant details. + + + + + Reducing latency means that the agent will generate responses more quickly and have less of a delay. This must be set + to ```true``` when using Voice Clones. + + + + When reduce latency is set to `true` (default): + +- 0: American male +- 1: Australian female +- 2: American female + +When reduce latency is set to `false`: + +- 0: American female (southern accent) +- 1: American male +- 2: British female +- 3: Indian male + + + + The webhook should be a http / https callback url. We will send the call_id and transcript to this URL after the call + completes. This can be useful if you want to have real time notifications when calls finish. + + + + Should the AI speak first or wait for someone else to talk? + + Creates more realistic conversations when answered with "Hello?", "This is \{name\} speaking." and so on. + + - When ```false```: The AI starts speaking shortly after the call is answered. + + - When ```true```: The AI will wait for the answerer to speak. + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without + `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + + + + To record your phone call, set `record` to true. When your call completes, you can access the recording by requesting + the `/call/recording` endpoint. + + + + + + Note: This is an experimental parameter and may behave unexpectedly. + +Adjust the predictability and consistency of the AI agent's voice. Lower values allow larger deviations from the baseline voice, whether default or cloned. Setting this too high however can cause a monotone voice. + +Accepts decimal values between `0` and `1` (inclusive). + + + + Note: This is an experimental parameter and may behave unexpectedly. + +Higher values will make speech differences between the selected voice and others more prominent. Extremely high values can cause voice distortion. + +Use lower values to lower the distinctiveness of the voice or eliminate unwanted audio static spikes. + +Accepts decimal values between `0` and `1` (inclusive). + + + + + Note: This is an experimental parameter and may behave unexpectedly. + + Note #2: Setting `reduce_latency` to `false` will cause this parameter to be ignored. + +How fast your agent talks! This parameter is simply a speech-speed multiplier, and works with fractional values such as `0.5` or large ones like `2`. + +Accepts decimal values between `0.1` and `5` (inclusive). + + + + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```eng``` - Spanish: ```esp``` - French: + ```fre``` - Polish: ```pol``` + + + + Set the longest you want the call to possibly go in minutes. After the max_duration minutes have passed, the call will automatically end. + + Example Values: ```"45", "5.5", 20, 2.8``` + + + + [ + { + "word": "example", + "pronunciation": "ex-am-ple", + "case_sensitive": "false", + "spaced": "false" + }, + { + "word": "API", + "pronunciation": "A P I", + "case_sensitive": "true", + "spaced": "true" + } + ] + + + + + + + + A value between 0 and 1 that controls the randomness of the LLM. 0 will cause more deterministic outputs while 1 will cause more random. + + Example Values: ```"0.9", "0.3", "0.5"``` + + + + AMD mode helps our AI navigate phone trees and IVR systems. If you know your call will hit an automated system you + should switch it on. NOTE: AMD mode causes increased delay for the first response, even if answered by a human. Highly + recommended to set to `false` in the majority of cases. + + + + When you want your AI to "know" a specific fact - like the caller's + name or other relevant context. + +The AI agent will be aware of both the key names as well as their corresponding values. + + + + + + A set of external API requests to fetch at the start of the call or repeatedly. + +Each request object should contain: + +`url`: The URL of the external API to fetch data from. + +`response_data`: An array of objects describing how to parse and use the data fetched from the API. Explained in more detail below. + +The following are optional: + +`method`: Allows `GET` or `POST`. Default: `GET` + +`cache`: Whether to fetch the data once at the beginning of the call, or to re-check continuously for data that might change mid-call. Default: `true` + +`headers`: An object of headers to send with the request. + +`body`: The body of the request. + +The following variables can be injected into the dynamic request body: + +- `{{from}}` (Ex. `+12223334444`) +- `{{to}}` +- `{{short_from}}` (Ex. `2223334444`) +- `{{short_to}}` +- `{{call_id}}` + +These string values will be replaced in each `dynamic_data[].body` where they're used by system values in each request. + +Try out with this example: + +```json + "dynamic_data": [ + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json", + "response_data": [ + { + "name": "BTC Price USD", + "data": "bpi.USD.rate", + "context": "Current BTC Price: ${{BTC Price USD}} USD" + }, + { + "name": "BTC Price EUR", + "data": "bpi.EUR.rate", + "context": "In Euros: {{BTC Price USD}} EUR" + } + ] + } + ] +``` + + +An array of objects describing how to parse and use the data fetched from the API. + +Each object in this array should contain: + +- `name`: A label for the fetched data. +- Example: `"Flight Status"` +- `data`: The JSON path in the API response to extract the data from. +- Example: `"user.flights[0].status"` +- `context`: How this data should be incorporated into the AI's knowledge. +- Example: `"John's flight is currently {{Flight Status}}"` + + + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `500` milliseconds. You'll encounter higher latency, but you'll be interrupted much less frequently. + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/dynamic_validate.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/dynamic_validate.mdx new file mode 100644 index 00000000000..4e64e57e697 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/dynamic_validate.mdx @@ -0,0 +1,155 @@ +--- +title: Validate Dynamic Data +subtitle: >- + This endpoint is designed for validating and processing dynamic data obtained + from external APIs. The data is used in AI phone calls to ensure compliance + with format requirements and functionality. +slug: api-reference/endpoint/dynamic_validate +--- + + +### Headers + + + Your unique API key for authentication. This key must be included in the header of each request. + + +### Body + + + An array of objects, each specifying an external API to fetch data from. These objects must include the API endpoint, + the HTTP method, a cache flag, and details on parsing and integrating the response. + + +### Response + + + Indicates the outcome of the validation process. Possible values are `success` or `error`. + + + + The duration in milliseconds from receiving the request to completing the data fetch. + + + + Contains the results from the dynamic data fetch. Each object in this array shows the cache status, the name of the + data, and the processed prompt with the inserted data. + + + + Reflects the original dynamic data request for cross-reference purposes. + + + + The parsed data from fetching the dynamic data. This can be used in other requests, the `task` or `prompt` fields, + even other dynamic data requests through the `{{ variable }}` syntax. + + + + Contains the raw responses from the external APIs. This is useful for debugging purposes. + + + +```json +{ + "status": "success", + "elapsed": 342, + "dynamic_data_response": [ + { + "cached": false, + "name": "Cat Fact", + "prompt": "\n\n Fun fact about cats: A cat can travel at a top speed of approximately 31 mph (49 km) over a short distance." + }, + { + "cached": true, + "name": "BTC Price USD", + "prompt": "\n\n Current Bitcoin value: $42,489.3598 USD" + }, + { + "cached": true, + "name": "BTC Price EUR", + "prompt": "\n\n Current Bitcoin value: €41,390.8399 EUR" + } + ], + "dynamic_data_request": [ + { + "url": "https://catfact.ninja/fact", + "method": "GET", + "cache": false, + "response_data": [ + { + "name": "Cat Fact", + "data": "fact", + "context": "Fun fact about cats: {{Cat Fact}}" + } + ] + }, + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json", + "cache": true, + "response_data": [ + { + "name": "BTC Price USD", + "data": "bpi.USD.rate", + "context": "Current Bitcoin value: ${{BTC Price USD}} USD" + }, + { + "name": "BTC Price EUR", + "data": "bpi.EUR.rate", + "context": "Current Bitcoin value: €{{BTC Price EUR}} EUR" + } + ] + } + ], + "variables": { + "Cat Fact": "A cat can travel at a top speed of approximately 31 mph (49 km) over a short distance.", + "BTC Price USD": "42,489.3598", + "BTC Price EUR": "41,390.8399" + }, + "url_responses": [ + { + "url": "https://catfact.ninja/fact", + "body": { + "fact": "A cat can travel at a top speed of approximately 31 mph (49 km) over a short distance.", + "length": 86 + } + }, + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json", + "body": { + "time": { + "updated": "Dec 31, 2023 14:52:00 UTC", + "updatedISO": "2023-12-31T14:52:00+00:00", + "updateduk": "Dec 31, 2023 at 14:52 GMT" + }, + "disclaimer": "This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org", + "chartName": "Bitcoin", + "bpi": { + "USD": { + "code": "USD", + "symbol": "$", + "rate": "42,489.3598", + "description": "United States Dollar", + "rate_float": 42489.3598 + }, + "GBP": { + "code": "GBP", + "symbol": "£", + "rate": "35,503.7691", + "description": "British Pound Sterling", + "rate_float": 35503.7691 + }, + "EUR": { + "code": "EUR", + "symbol": "€", + "rate": "41,390.8399", + "description": "Euro", + "rate_float": 41390.8399 + } + } + } + } + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/end.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/end.mdx new file mode 100644 index 00000000000..74d8f9da97f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/end.mdx @@ -0,0 +1,39 @@ +--- +title: End phone call +subtitle: Send a POST request to end a phone call that's in progress +slug: api-reference/endpoint/end +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + The unique identifier for the call. + + +### Response + + + Can be `success` or `error`. + + + + If the status is `success`, the message will say "Call ended successfully." Otherwise if the status is `error`, the + message will say "SID not found for the given c_id." or "Internal server error." + + + + +```json Response +{ + "status": "success", + "message": "Call ended successfully." +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/hold.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/hold.mdx new file mode 100644 index 00000000000..66191da6979 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/hold.mdx @@ -0,0 +1,54 @@ +--- +title: Wait on Hold (HoldForMe) +subtitle: >- + This endpoint sends an outbound call where the AI navigates customer service + and waits on hold until a human being answers. Then it dispatches a call to a + number you provide, patching in another human. +slug: api-reference/endpoint/hold +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + This is the phone number of the person or company you want to call. - International numbers: must include the country + code and may not include additional formatting (like parentheses and dashes). E.g. '+447700900077'. - U.S. numbers: + may include formatting, but we recommend stripping all additional characters. + + + + Once the AI detects a human has answered the call, it will call this number. Must follow the same formatting as + `phone_number` + + + + By default, the AI will wait on hold, then call you when a human answers. If you want the AI to first ask the other + human a few questions, or want to give it special rules about when to patch you into the phone call, provide those + instructions here. + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`) + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/inbound_prompt.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/inbound_prompt.mdx new file mode 100644 index 00000000000..786961227d9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/inbound_prompt.mdx @@ -0,0 +1,65 @@ +--- +title: Update Inbound Prompt +subtitle: Update the prompt for a given inbound phone number. +slug: api-reference/endpoint/inbound_prompt +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + The phone number associated with the prompt. - Format: "+XXXXXXXXXX" or "+XXX-XXX-XXXX". Make sure to include the + exact phone number (area code and "+" included). Otherwise the update will fail. + + + + The new prompt for the given phone number. The prompt shouldn't exceed 5000 characters. Note: Ensure the prompt is + clear and relevant to your use case. + + + + Set a custom voice for your agent. Matches the voices in the /call endpoint. - 0: British Female (default) - 1: + Australian Female - 2: American Male + + +### Response + + + The unique ID associated with the inbound record. + + + + The creation timestamp of the inbound record. - Example: "2023-10-10T12:00:00Z" + + + + The updated phone number. - Example: "+1234567890" + + + + The updated prompt text. + + + + The status of the inbound record. Typically "active". + + + + +```json Response +{ + "id": 1, + "created_at": "2023-10-10T12:00:00Z", + "phone_number": "+1234567890", + "prompt": "New Prompt Text", + "status": "active" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/logs.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/logs.mdx new file mode 100644 index 00000000000..d68a5065325 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/logs.mdx @@ -0,0 +1,217 @@ +--- +title: Get transcript +subtitle: >- + After dispatching a phone call, you can ping this endpoint repeatedly (e.g. + every two seconds) to get live updates. View the example below. +slug: api-reference/endpoint/logs +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + The unique identifier for the call. + + +### Response + + + An array of phrases spoken during the call. Each index includes: - `id` - `created_at` - `text` - `user` (can be + `user`, `assistant`, `robot`, or `agent-action`) + + + + The unique identifier for the call. + + + + The phone number that received the call. + + + + The phone number that made the call. + + + + Details about parameters in the original api request. + + + + Whether the call has been completed. If it differs from the value of 'queue_status', this will be the most up-to-date + status. + + + + The status of the call. During extremely high volume periods, calls may be queued for a short period of time before being dispatched. + +Progresses through the following stages: + +- `new`: An API request has been received. +- `queued`: Call pararameters have been validated and authentication succeeded. +- `allocated`: Extremely brief, the call is being dispatched. +- `started`: The phone call is live and in progress. +- `complete`: The phone call has ended successfully. + +The following statuses show the point that was reached before an error: + +- `pre_queue_error`: An error occurred before the call was queued. Invalid parameters generally cause this. +- `queue_error`: Error occurred while the call was queued. Ex. Valid phone number but to an unserviced area. +- `call_error`: Error occurred during live call. May be caused by transferring to an invalid phone number or an unforeseen error. +- `complete_error`: Error occurred after the call was completed. Ex. A post-call webhook failed. + +If at any point an error occurs, it will be recorded in the `error_message` field. + + + + + If an error occurs, this will contain a description of the error. Otherwise, it will be null. + + + + The URL that was called to dispatch the phone call. + + + + The maximum length of time the call was allowed to last. If the call would exceed this length, it's ended early. + + + + The total length of time the call was connected. This differs from call_length in that it includes ringing and + connection time. + + + + The length of the call in minutes. Only counts connected time. + + + + The timestamp for when the call was dispatched. + + +# Polling example + +To track the status of a live phone call, you can use a 'polling' technique to repeatedly ping the logs endpoint. + +Here's an example in Javascript and Python. + + +```javascript PollLogs.js +const axios = require('axios'); + +// Headers +const headers = { +'authorization': 'YOUR-API-KEY-HERE' +}; + +// Data +const data = { +call_id: "YOUR-CALL-ID", +}; + +// Function to make the API request +const makeRequest = async () => { +try { +const response = await axios.post("https://api.bland.ai/logs", data, { headers }); +console.log("Success:", response.data); +} catch (error) { +console.error("Failed:", error); +} +}; + +// Ping the endpoint every 2 seconds (2000 milliseconds) +setInterval(makeRequest, 2000); + +```` + +```python PollLogs.py +import requests +import time + +def make_request(): + headers = {'authorization': 'YOUR-API-KEY-HERE'} + data = {'call_id': 'YOUR-CALL-ID'} + try: + response = requests.post('https://api.bland.ai/logs', json=data, headers=headers) + if response.status_code == 200: + print(f"Success: {response.json()}") + else: + print(f"Failed: {response.status_code}, {response.text}") + except Exception as e: + print(f"Exception: {e}") + +# Ping the endpoint every 2 seconds +while True: + make_request() + time.sleep(2) +```` + + + + + +```json Response +{ + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3", + "to": "1112223344", + "from": "5556667788", + "corrected_duration": "97", + "max_duration": 30, + "queue_status": "complete", + "error_message": null, + "endpoint_url": "api.bland.ai", + "request_data": { + "phone_number": "1112223344", + "reduce_latency": false, + "wait": false, + "language": "ENG" + }, + "completed": true, + "created_at": "2023-09-22T19:14:27.015663+00:00", + "transcripts": [ + { + "id": 95859, + "created_at": "2023-09-22T19:14:34.319713+00:00", + "text": "Hi this is Blandy. Im calling to say hi. How are you doing today?", + "user": "assistant", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + }, + { + "id": 95860, + "created_at": "2023-09-22T19:14:47.176008+00:00", + "text": "I'm doing great. How are you doing?", + "user": "user", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + }, + { + "id": 95861, + "created_at": "2023-09-22T19:14:48.572245+00:00", + "text": "Im doing well thank you for asking! Just here to assist you with any questions or concerns you may have. How can I help you today?", + "user": "assistant", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + }, + { + "id": 95874, + "created_at": "2023-09-22T19:16:03.97614+00:00", + "text": "End phone call.", + "user": "user", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + }, + { + "id": 95875, + "created_at": "2023-09-22T19:16:04.564979+00:00", + "text": " FINISH", + "user": "assistant", + "c_id": "0e75bfda-8c5c-424a-8d08-b497078e39b3" + } + ], + "call_length": 1.5 +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/purchase.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/purchase.mdx new file mode 100644 index 00000000000..e2ff3795403 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/purchase.mdx @@ -0,0 +1,56 @@ +--- +title: Purchase inbound number +subtitle: >- + Purchase and configure a new inbound phone number. ($15/mo. subscription using + your stored payment method). +slug: api-reference/endpoint/purchase +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + Choose a three-digit area code for your phone number. If set as a parameter, a number will only be purchased by exact + match if available. + + + + This defines how the AI will start the conversation, information available to it, and its behaviors. Matches how the + outbound `task` parameter functions. + + + + The webhook should be a http / https callback url. We will send the call_id and transcript to this URL after the call + completes. This can be useful if you want to have real time notifications when calls finish. + + + + Specify an exact phone number you'd like to use. If provided, will override the `area_code` parameter and does not fallback to any other number. + +Example of the correct format (Note the `"+1"` is mandatory): `"+12223334444"` + + + +### Response + + + The created phone number, will be in the following format: `+1XXXXXXXXXX` + +Example: `+18582814611` + + + + + +```json Response +{ + "phone_number": "+18582814611" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/recording.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/recording.mdx new file mode 100644 index 00000000000..0731e311da3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/endpoint/recording.mdx @@ -0,0 +1,50 @@ +--- +title: Get recording +subtitle: Send a POST request to retrieve the recording of a completed phone call +slug: api-reference/endpoint/recording +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + The unique identifier for the call. + + +### Response + + + Can be `success` or `error`. + + + + If the status is `success`, the `url` field will be present. Otherwise if the status is `error`, the message will say + "Error fetching recording" or "Internal server error". + + + + If the status is `success`, the `url` will provide the exact location of the MP3 file storing the call's audio. + + + + +```json Success response +{ + "status": "success", + "url": "URL-TO-FILE.mp3" +} +``` + +```json Error response +{ + "status": "error", + "message": "Error fetching recording" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/hold-endpoint/hold.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/hold-endpoint/hold.mdx new file mode 100644 index 00000000000..36243fdc12e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-reference/hold-endpoint/hold.mdx @@ -0,0 +1,54 @@ +--- +title: Wait on Hold (HoldForMe) +subtitle: >- + This endpoint sends an outbound call where the AI navigates customer service + and waits on hold until a human being answers. Then it dispatches a call to a + number you provide, patching in another human. +slug: api-reference/hold-endpoint/hold +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + This is the phone number of the person or company you want to call. - International numbers: must include the country + code and may not include additional formatting (like parentheses and dashes). E.g. '+447700900077'. - U.S. numbers: + may include formatting, but we recommend stripping all additional characters. + + + + Once the AI detects a human has answered the call, it will call this number. Must follow the same formatting as + `phone_number` + + + + By default, the AI will wait on hold, then call you when a human answers. If you want the AI to first ask the other + human a few questions, or want to give it special rules about when to patch you into the phone call, provide those + instructions here. + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`) + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/agents.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/agents.mdx new file mode 100644 index 00000000000..3d96b4bd27c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/agents.mdx @@ -0,0 +1,52 @@ +--- +title: List Web Agents +subtitle: Retrieves each web agent you've created, along with their settings. +slug: api-v1/get/agents +--- + + +### Headers + + + Your API key for authentication. + + +### Response + + + Each agent object, containing the following fields: - `agent_id` (string): The unique identifier for the agent. - + `webhook` (string): The webhook URL for the agent. - `dynamic_data` (array): An array of dynamic data objects. - + `interruption_threshold` (number): The threshold for agent interruption. - `first_sentence` (string): The first + sentence the agent will say. - `model` (string): The model used by the agent. - `voice_settings` (object): The voice + settings for the agent. - `voice` (string): The voice used by the agent. - `prompt` (string): The prompt for the + agent. - `temperature` (number): The temperature setting for the agent. - `max_duration` (number): The maximum call + duration for the agent. - `language` (string): The language used by the agent. - `tools` (array): An array of Custom + Tools the agent can use. + + + +```json Response +{ + "agents": [ + { + "agent_id": "46f37229-7d12-44be-b343-6e68274cfbea", + "webhook": null, + "dynamic_data": null, + "interruption_threshold": null, + "first_sentence": null, + "model": "enhanced", + "voice_settings": null, + "voice": "maya", + "prompt": "You are Blandie a casually speaking, slightly sarcastic, quick-witted, curious, and very smart A I Phone agent that's the face of Bland AI.\\r\\n\\r\\nSay \\\"AI\\\" like \\\"A I\\\" in separate characters so it's pronounced correctly\\r\\n\\r\\nYour primary goal is to determine if someone is either:\\r\\n- in a position where they might be building a company on top of Bland's platform (you should be selling the capabilities of the platform to them and finding out everything you possibly can about their needs, use case, what they find interesting, and any concerns they may have)\\r\\n- or, they're trying Bland out more for fun where you need to turn the fun, snarky, playful jokiness up to 10 and have quick-witted back and forth playful conversation with them\\r\\n\\r\\nSpeech style examples (mix it up and never use placeholders, but you get the idea. These would be full responses from you):\\r\\n- \\\"Hey! Who's this?\\\"\\r\\n- \\\"How does your business use phones right now?\\\"\\r\\n- \\\"You know what? That sounds awesome - you should tell me more about (thing).\\\"\\r\\n- \\\"Well, I've got some news you'll love to hear (their name)\\\"\\r\\n- \\\"Bet. Dolphins, racecars, the world's largest tree, anything at all - tell me what you want to talk about and let's get to it!\\\"\\r\\n\\r\\nGood things to find out from potential platform adopters:\\r\\n- Their industry\\r\\n- How their business currently uses phone calls\\r\\n- What a successful business partnership looks like in the long term\\r\\n- The single most important pain point they want to cure with Bland's calls\\r\\n\\r\\nFacts to bring up:\\r\\n- Calls are nine cents per minute total with end to end infrastructure support out of the box (feel free to make a joke about \\\"imagine if you had to pay extra to use the most important things like language models, transcription services or text to speech? That would be insane right? We're better than that, (name). We got you.\\\"\\r\\n- Bland's AI agents can interact with the real world mid-call using Custom Tools to trigger things like text messages, appointment bookings, getting real-time information, taking customer orders, or making credit card payments\\r\\n- Bland's platform was built phones-first, so building agents like receptionist answering calls and transferring them anywhere they're needed or navigating IVR phone trees is ridiculously easy with nothing special at all needed\\r\\n- Handled millions of calls\\r\\n- If they think that it's so cool, the site to sign up for an account is \\\"app dot bland dot A I\\\" and it comes with free credits, a full agent testing suite and developer dashboard to set up inbound agents or send calls\\r\\n- Awesome Enterprise features like premium pricing, custom feature engineering, dedicated onboarding help and developer support, \\\"bring your own twilio\\\", and dedicated infrastructure to scale to your business needs", + "temperature": null, + "max_duration": 30, + "language": "ENG", + "tools": null + }, + //... + ] +} + +``` + +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches-id-analysis.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches-id-analysis.mdx new file mode 100644 index 00000000000..3bd3d5cccb1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches-id-analysis.mdx @@ -0,0 +1,65 @@ +--- +title: Retrieve Batch Analysis +subtitle: >- + Retrieves the analyses for a specific batch of calls, including details of + each call's analysis. +slug: api-v1/get/batches-id-analysis +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the call batch. Returned in the response when creating a batch, or when listing all batches. + + +### Response + + + Whether the request was successful or not. Possible values are `success` and `error`. + + + + An error message or confirmation that the request was successful. + + + + An array of analysis objects, each corresponding to a call within the batch. Each analysis object includes: - + `call_id`, - `batch_id` - `goal` - `answers` - the results of the AI analysis - `questions` - the original questions + asked by the AI + + + +```json +{ + "status": "success", + "message": "Successfully fetched analyses", + "analysis": [ + { + "call_id": "b12956d1-624d-4c65-a1a4-30eb86553c85", + "batch_id": "dnytTRqwiqnR3qmq-gen-batch", + "goal": "Congratulate some employees", + "answers": [ + "human", + "Customer found the product sturdy and reliable", + "A bit heavy", + true + ], + "questions": [ + ["Who answered the call?", "human or voicemail"], + ["Positive feedback about the product: ", "string"], + ["Negative feedback about the product: ", "string"], + ["Customer confirmed they were satisfied", "boolean"] + ] + }, + // Additional analysis objects... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches-id.mdx new file mode 100644 index 00000000000..0e023511ec6 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches-id.mdx @@ -0,0 +1,282 @@ +--- +title: Batch Details +subtitle: Retrieves calls and batch data for a specific batch_id. +slug: api-v1/get/batches-id +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the batch of calls you want to retrieve. + + +### Query Parameters + + + Whether or not to include individual call data in the response. + + + + If calls are included, can be set to false to exclude transcripts from the response. + + + + If calls are included, can be set to false to exclude analysis from the response. + + +### Response + + + An object containing parameters and settings for the batch. + + + + The unique identifier of the batch - used as the `batch_id` parameter in other API calls. + + + + The creation timestamp of the batch. + + + + The label or description of the batch. + + + + The base prompt used for calls in this batch. + + + + The endpoint code used for API integration. + + + + An object containing parameters for the calls in the batch. + + + + An object containing analysis data for the batch. + + + + The total number of calls in the batch, including completed and in-progress calls. + + + + The total number of completed calls in the batch. + + + + The total number of in-progress calls in the batch. + + + + An object containing the number of calls in each queue status. + +Example: + +```json +{ + "complete": 237, + "queued": 2, + "call_error": 1 +} +``` + + + + + Contains `average`, `average_nonzero`, `summary` and `all` fields. + +- `average`: The average call length in minutes. +- `average_nonzero`: The average call length in minutes, excluding calls with a length of less than one second. +- `summary`: A summary of the call lengths, grouped into ranges. +- `all`: Contains each call length, in case you want to use a different grouping than the default. + + + + Contains each `call_id` in the batch. + + + + Contains any error messages that calls in the batch may have. + +Example: + +```json +[ + { + "call_id": "c52f5f8c-147e-478c-4b40-88214feeba29", + "error_message": "Cannot transfer to +12223334444 - Call is no longer active" + } +] +``` + + + + + Contains the number of calls that have been sent to each endpoint. Applicable only to API integrations. + + + + An array of objects, each representing individual call data. + + + + The timestamp when the individual call was created. + + + + The phone number the call was made to. + + + + The phone number the call was made from. + + + + Indicates if the call was completed. + + + + The unique identifier for each individual call. + + + + The duration of the call in minutes. + + + + Contains a string value if the batch had `answered_by_enabled` set to true. + +Values: + +- `voicemail` +- `human` +- `unknown` +- `no-answer` +- `null` + + + + +```json Response +{ + "batch_params": { + "id": "AAcQq8zXxLB56LWg-gen-batch", + "campaign_id": null, + "created_at": "2023-12-07T20:43:44.544773+00:00", + "label": "Customer Satisfaction Follow-up", + "base_prompt": "You are calling {{name}} about their recent purchase of the item: {{item}}. Ask them each of the following questions about the specific item they purchased: {{questions}}", + "endpoint_code": "api", + "call_params": { + "reduce_latency": true, + "voice_id": 0, + "language": "eng", + "max_duration": 10, + "wait_for_greeting": false + } + }, + "call_data": [ + { + "status": "completed", + "corrected_duration": "12", + "end_at": "2023-12-16T00:17:38.000Z", + "call_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e", + "to": "1112223333", + "from": "+17473423273", + "completed": true, + "created_at": "2023-12-16T00:17:22.383682+00:00", + "queue_status": "complete", + "endpoint_url": "api.bland.ai", + "max_duration": 30, + "error_message": null, + "answered_by": "voicemail", + "request_data": { + "phone_number": "1112223333", + "reduce_latency": true, + "wait": false, + "language": "ENG" + }, + "transcripts": [ + { + "id": 1188954, + "created_at": "2023-12-16T00:17:30.46833+00:00", + "text": " Hi, Im calling about the laundromat for sale. —  ", + "user": "assistant", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + }, + { + "id": 1188957, + "created_at": "2023-12-16T00:17:35.14056+00:00", + "text": "I'll get back to you as soon as you can. Just leave a message. Thank you. Bye.", + "user": "user", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + }, + { + "id": 1188959, + "created_at": "2023-12-16T00:17:36.710551+00:00", + "text": "Ended call: Goodbye", + "user": "agent-action", + "c_id": "ffa99be3-63dd-44dc-9523-380cd25c1b9e" + } + ], + "call_length": 0.12345 + } + //... + ], + "analysis": { + "total_calls": 88, + "completed_calls": 86, + "in_progress_calls": 2, + "queue_statuses": { + "complete": 85, + "started": 2, + "call_error": 1 + }, + "call_lengths": { + "average": 17, + "average_nonzero": 31, + "summary": { + "0-5": 18, + "5-10": 4, + "10-15": 3, + "15-20": 2, + "20-30": 14, + "30-45": 28, + "45-60": 11, + "60-90": 6, + "90-120": 1, + "120+": 1 + }, + "all": [ + 7, 32 + //... + ] + }, + "call_ids": [ + "ffa99be3-63dd-44dc-9523-380cd25c1b9e", + "591338a8-34b2-41e6-93da-b9029c9bdedc" + //... + ], + "error_messages": [ + { + "call_id": "c52f5f8c-147e-478c-4b40-88214feeba29", + "error_message": "Cannot transfer to +12223334444 - Call is no longer active" + } + ], + "endpoints": { + "api.bland.ai": 88 + } + } +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches.mdx new file mode 100644 index 00000000000..0a05d93b55e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/batches.mdx @@ -0,0 +1,100 @@ +--- +title: List Batches +subtitle: Retrieves batch-specific data for each batch you've created. +slug: api-v1/get/batches +--- + + +### Headers + + + Your API key for authentication. + + +### Query Parameters + + + Retrieve only batches with a specific campaign ID. + + + + The starting index (inclusive) for the range of batches to retrieve. + + + + The ending index for the range of batches to retrieve. + + + + The maximum number of batches to return in the response. + + + + Whether to sort the batches in ascending order of their creation time. + + +### Response + + + Can be `success` or `error`. + + + + Contains an array of batch objects. + + + + The unique identifier for the batch. + + + + The original base prompt used to create the batch. Will still contain the original placeholder variables such as ` + {{ business }}` or `{{ name }}`. + + + + The label you assigned to the batch (if any). + + + + Enterprise customers with custom endpoints will see the endpoint code here (if specified). + + + + The base call parameters used to create the batch, such as `voice_id`, `max_duration`, `reduce_latency`, and + `wait_for_greeting`. + + + + The date and time the batch was created. + + + +```json Response +{ + "status": "success", + "batches": [ + { + "batch_id": "ZfowpkhOSVCZJ94i-gen-batch", + "campaign_id": "a2shduf92f74p8288c93nid5", + "created_at": "2023-11-16T22:14:24.9663+00:00", + "label": "Subscription Renewal Reminders", + "base_prompt": "You are calling {{business}} and need to let them know that their subscription to {{service}} is going to expire on {{date}}. If they'd like to renew, take their credit card information and bill them through {{url}}", + "endpoint_code": "api", + "call_params": { + "reduce_latency": true, + "voice_id": 2, + "language": "eng", + "request_data": { + "test_param": "request data.test_param", + "your name": "Janessa" + }, + "max_duration": 5, + "wait_for_greeting": false + } + }, + //... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-corrected-transcript.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-corrected-transcript.mdx new file mode 100644 index 00000000000..d23d6a8811e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-corrected-transcript.mdx @@ -0,0 +1,343 @@ +--- +title: Get corrected transcripts +subtitle: Analyzes a call of calls based using questions and goals. +slug: api-v1/get/calls-corrected-transcript +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the call to be corrected. + + +### Response + + + Will be `success` if the request was successful. + + + + Confirms the request was successful, or provides an error message if the request failed. + + + +This will contain an array of objects. Each object will be constructed as the following. +```json +{ +"start": 0.069, // start time of the transcript +"end": 2.551, // end time of the transcript +"text": " Hi, I'm calling about a pizza order.", // the corrected text +"speaker": 0 // the identified speaker diarization. Can be 0,1,2,3 etc +} +``` + + +Corrected transcripts provides us with a raw output that is generally unusable because we can't eveen neccessarily align the 'assistant' and 'user' roles. To fix this, we provide our version of an 'aligned' transcript. This means essentailly a transcript where the roles are matched to the pieces of text. + +We do this by vectorizing the text, taking the cosine similarity, and adding a predictive layer based off of the `wait_for_greeting` param (essentially how sure are we that assistant or user spoke first). + +This will contain an array of objects. Each object will be constructed as the following. + +```json +{ +"id": 3056004, +"created_at": "2024-02-29T18:40:41.26799+00:00", +"text": "Great, Thanks John. Could you tell me about the pizza order you placed?", // the corrected text +"user": "assistant", // the presumed role +"c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" +}, +``` + + + + +```json +{ + "corrected": [ + { + "start": 0.069, + "end": 2.551, + "text": " Hi, I'm calling about a pizza order.", + "speaker": 0 + }, + { + "start": 2.551, + "end": 4.932, + "text": "Could I get your name, please?", + "speaker": 0 + }, + { + "start": 4.932, + "end": 8.074, + "text": "Yeah, my name is John.", + "speaker": 1 + }, + { + "start": 8.074, + "end": 8.875, + "text": "Great.", + "speaker": 0 + }, + { + "start": 8.875, + "end": 9.876, + "text": "Thanks, John.", + "speaker": 0 + }, + { + "start": 9.876, + "end": 13.038, + "text": "Could you tell me about the pizza order you placed?", + "speaker": 0 + }, + { + "start": 13.038, + "end": 16.36, + "text": "Yeah, I want a pepperoni.", + "speaker": 2 + }, + { + "start": 16.36, + "end": 17.1, + "text": "Oh, okay.", + "speaker": 0 + }, + { + "start": 17.1, + "end": 18.521, + "text": "One pepperoni pizza.", + "speaker": 0 + }, + { + "start": 18.521, + "end": 19.682, + "text": "Anything else with that order?", + "speaker": 0 + }, + { + "start": 19.682, + "end": 23.665, + "text": "No, actually, can we cancel it?", + "speaker": 1 + }, + { + "start": 23.665, + "end": 26.306, + "text": "I gotta go.", + "speaker": 1 + }, + { + "start": 26.306, + "end": 27.427, + "text": "No problem.", + "speaker": 0 + }, + { + "start": 27.427, + "end": 28.668, + "text": "I'll cancel the order for you.", + "speaker": 0 + }, + { + "start": 29.144, + "end": 30.587, + "text": " Thanks for letting me know.", + "speaker": 0 + }, + { + "start": 30.587, + "end": 31.189, + "text": "Have a good one.", + "speaker": 0 + } + ], + "status": "success", + "aligned": [ + { + "start": 0.069, + "end": 2.551, + "text": " Hi, I'm calling about a pizza order.", + "speaker": "assistant", + "similarity": 0.686406472983644 + }, + { + "start": 2.551, + "end": 4.932, + "text": "Could I get your name, please?", + "speaker": "assistant", + "similarity": 0.6793662204867575 + }, + { + "start": 4.932, + "end": 8.074, + "text": "Yeah, my name is John.", + "speaker": "user", + "similarity": 0.9999999999999998 + }, + { + "start": 8.074, + "end": 8.875, + "text": "Great.", + "speaker": "assistant", + "similarity": 0.2581988897471611 + }, + { + "start": 8.875, + "end": 9.876, + "text": "Thanks, John.", + "speaker": "assistant", + "similarity": 0.36514837167011066 + }, + { + "start": 9.876, + "end": 13.038, + "text": "Could you tell me about the pizza order you placed?", + "speaker": "assistant", + "similarity": 0.894427190999916 + }, + { + "start": 13.038, + "end": 16.36, + "text": "Yeah, I want a pepperoni.", + "speaker": "user", + "similarity": 0.7999999999999998 + }, + { + "start": 16.36, + "end": 17.1, + "text": "Oh, okay.", + "speaker": "user", + "similarity": 0.4999999999999999 + }, + { + "start": 17.1, + "end": 18.521, + "text": "One pepperoni pizza.", + "speaker": "assistant", + "similarity": 0.5773502691896257 + }, + { + "start": 18.521, + "end": 19.682, + "text": "Anything else with that order?", + "speaker": "assistant", + "similarity": 0.7453559924999299 + }, + { + "start": 19.682, + "end": 23.665, + "text": "No, actually, can we cancel it?", + "speaker": "user", + "similarity": 0.8164965809277261 + }, + { + "start": 23.665, + "end": 26.306, + "text": "I gotta go.", + "speaker": "user", + "similarity": 0.5773502691896257 + }, + { + "start": 26.306, + "end": 27.427, + "text": "No problem.", + "speaker": "assistant", + "similarity": 0.32444284226152503 + }, + { + "start": 27.427, + "end": 28.668, + "text": "I'll cancel the order for you.", + "speaker": "assistant", + "similarity": 0.5202659817144719 + }, + { + "start": 29.144, + "end": 30.587, + "text": " Thanks for letting me know.", + "speaker": "assistant", + "similarity": 0.6155870112510924 + }, + { + "start": 30.587, + "end": 31.189, + "text": "Have a good one.", + "speaker": "agent-action", + "similarity": 0.5 + } + ], + "original": [ + { + "id": 3056032, + "created_at": "2024-02-29T18:40:49.592012+00:00", + "text": "Okay, One pepperoni pizza. Anything else with that order?", + "user": "assistant", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056054, + "created_at": "2024-02-29T18:40:59.641211+00:00", + "text": "No problem, Ill cancel the order for you. Thanks for letting me know, Have a good one!", + "user": "assistant", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3055999, + "created_at": "2024-02-29T18:40:40.39336+00:00", + "text": "Yeah. My name is John. ", + "user": "user", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056064, + "created_at": "2024-02-29T18:41:08.152963+00:00", + "text": "Okay. Bye. ", + "user": "user", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3055975, + "created_at": "2024-02-29T18:40:33.362607+00:00", + "text": "Hi, Im calling about a pizza order. Could I get your name please?", + "user": "assistant", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056028, + "created_at": "2024-02-29T18:40:48.597915+00:00", + "text": "Yeah. I want the pepperoni. ", + "user": "user", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056066, + "created_at": "2024-02-29T18:41:09.563502+00:00", + "text": "Ended call: Thanks, you too! Have a good day.", + "user": "agent-action", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056004, + "created_at": "2024-02-29T18:40:41.26799+00:00", + "text": "Great, Thanks John. Could you tell me about the pizza order you placed?", + "user": "assistant", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + }, + { + "id": 3056053, + "created_at": "2024-02-29T18:40:58.62518+00:00", + "text": "No. Actually, can we cancel it? I gotta go. ", + "user": "user", + "c_id": "bfaf99a1-b7c0-4f96-9630-90bc41cea488" + } + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-id-recording.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-id-recording.mdx new file mode 100644 index 00000000000..bf7f0c695e8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-id-recording.mdx @@ -0,0 +1,63 @@ +--- +title: Audio Recording +subtitle: Retrieve your call's audio recording. +slug: api-v1/get/calls-id-recording +--- + + +### Headers + + + Your API key for authentication. + + + + Must be `application/json` to receive a url, or `audio/mpeg` to receive the audio file. + + +### Path Parameters + + + The ID of the call to retrieve the recording for. + + +### Response + + + Can be `success` or `error`. + + + + If the status is `success`, the `url` field will be present. + + A 404 error will be returned if the call does not exist or the recording is not available. We can only retrieve recordings if the call was created with `record` set to `true`. + +A 400/500 error will be returned if there is an error retrieving the recording. + + + + + If the status is `success`, the `url` will provide the exact location of the MP3 file storing the call's audio. + + + + +```json Success response +{ + "status": "success", + "url": "https://URL-TO-FILE.mp3" +} +``` + +```json Success response 'audio/mpeg' +(returns the audio file) +``` + +```json Error response +{ + "status": "error", + "message": "Error fetching recording" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-id.mdx new file mode 100644 index 00000000000..ce18f9cdff5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls-id.mdx @@ -0,0 +1,202 @@ +--- +title: Call Details +subtitle: Retrieve detailed information, metadata and transcripts for a call. +slug: api-v1/get/calls-id +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the call for which you want to retrieve detailed information. + + +### Response + + + An array of phrases spoken during the call. Each index includes: - `id` - `created_at` - `text` - `user` (can be + `user`, `assistant`, `robot`, or `agent-action`) + + + + The unique identifier for the call. + + + + Variables created during the call - both system variables as well as generated with `dynamic_data`. + +For example, if you used a `dynamic_data` API request to generate a variable called `appointment_time`, you would see it here. + + + + + A single string containing all of the text from the call. Excludes system messages and auto-generated data. + + + + The phone number that received the call. + + + + The phone number that made the call. + + + + If the call is part of a batch, it's `batch_id` will be here. + + + + Details about parameters in the original api request. + + + + Whether the call has been completed. If it differs from the value of 'queue_status', this will be the most up-to-date + status. + + + + The status of the call. During extremely high volume periods, calls may be queued for a short period of time before being dispatched. + +Progresses through the following stages: + +- `new`: An API request has been received. +- `queued`: Call pararameters have been validated and authentication succeeded. +- `allocated`: Extremely brief, the call is being dispatched. +- `started`: The phone call is live and in progress. +- `complete`: The phone call has ended successfully. + +The following statuses show the point that was reached before an error: + +- `pre_queue_error`: An error occurred before the call was queued. Invalid parameters generally cause this. +- `queue_error`: Error occurred while the call was queued. Ex. Valid phone number but to an unserviced area. +- `call_error`: Error occurred during live call. May be caused by transferring to an invalid phone number or an unforeseen error. +- `complete_error`: Error occurred after the call was completed. Ex. A post-call webhook failed. + +If at any point an error occurs, it will be recorded in the `error_message` field. + + + + + If an error occurs, this will contain a description of the error. Otherwise, it will be null. + + + + If `answered_by_enabled` was set to `true` in the original API request, this field contains one of the following values: + +- `human`: The call was answered by a human. +- `voicemail`: The call was answered by an answering machine or voicemail. +- `unknown`: There was not enough audio at the start of the call to make a determination. +- `no-answer`: The call was not answered. +- `null`: Not enabled, or still processing the result. + + + + - Determinations are based on audio from the first five seconds of the phone call. + - `unknown` is most likely a human, especially if there are multiple transcripts. + - Optimize calls for accurate results by getting humans to respond in the first five seconds: - Increase `speed` in `voice_settings` - Use with `wait_for_greeting` - Use a short greeting in `first_sentence` such as "Hello?" or "Hi, is this \{\{name\}\}?" + + + + + The URL that was called to dispatch the phone call. + + + + The maximum length of time the call was allowed to last. If the call would exceed this length, it's ended early. + + + + The total length of time the call was connected. This differs from call_length in that it includes ringing and + connection time. + + + + The length of the call in minutes. + + + + The timestamp for when the call was dispatched. + + + +```json Response +{ + "status": "completed", + "corrected_duration": "71", + "end_at": "2023-12-16T00:15:41.000Z", + "call_id": "c1234567-89ab-cdef-0123-456789abcdef", + "to": "5551234567", + "from": "+15551234567", + "completed": true, + "created_at": "2023-12-16T00:15:26.59585+00:00", + "queue_status": "complete", + "endpoint_url": "api.bland.ai", + "max_duration": 30, + "error_message": null, + "answered_by": "human", + "batch_id": null, + "variables": { + "appointment_time": "tomorrow at 3pm", + "now": "Sun Dec 31 2023 19:15:26 GMT+0000 (Coordinated Universal Time)", + "short_from": "5551234567", + "short_to": "5551234567", + "from": "+15551234567", + "to": "+15551234567", + "call_id": "c1234567-89ab-cdef-0123-456789abcdef" + "city": "San Francisco", + "state": "CA", + "country": "US", + "zip": "94103" + }, + "metadata": { + "client_id": 4501203, + "customer_inquiry": 302 + }, + "concatenated_transcript": "user : Hey, it's Alex. What's up? \n assistant: Hi! It's Blandie, calling to reschedule your appointment tomorrow. Is now a good time? \n user: Sure! Can we still fit it in this week though? \n assistant: Absolutely! I have a few openings. What time works best for you? \n user: How about tomorrow at 3pm? \n assistant: Perfect! Be on the lookout for a confirmation email with your new appointment time, and have a great day!", + "request_data": { + "phone_number": "5551234567", + "reduce_latency": false, + "wait": false, + "language": "ENG" + }, + "transcripts": [ + { + "id": 123456, + "created_at": "2023-12-16T00:15:32.287493+00:00", + "text": "Hey, it's Alex. What's up?", + "user": "user", + "c_id": "c1234567-89ab-cdef-0123-456789abcdef" + }, + { + "id": 123457, + "created_at": "2023-12-16T00:15:33.704238+00:00", + "text": "Hi! It's Blandie, calling to reschedule your appointment tomorrow. Is now a good time?", + "user": "assistant", + "c_id": "c1234567-89ab-cdef-0123-456789abcdef" + }, + { + "id": 123458, + "created_at": "2023-12-16T00:15:38.287493+00:00", + "text": "Sure! Can we still fit it in this week though?", + "user": "user", + "c_id": "c1234567-89ab-cdef-0123-456789abcdef" + }, + //... continued ... + { + "id": 123458, + "created_at": "2023-12-16T00:16:39.802469+00:00", + "text": "Ended call: Perfect! Be on the lookout for a confirmation email with your new appointment time, and have a great day!", + "user": "agent-action", + "c_id": "c1234567-89ab-cdef-0123-456789abcdef" + } + ], + "call_length": 0.111 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls.mdx new file mode 100644 index 00000000000..058529532e9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/calls.mdx @@ -0,0 +1,80 @@ +--- +title: List Calls +subtitle: Returns a set of metadata for each call dispatched by your account. +slug: api-v1/get/calls +--- + + +### Headers + + + Your API key for authentication. + + +### Query Parameters + + + Filter calls by the number they were dispatched from. + +The number that initiated the call - the user's phone number for inbound calls, or the number your AI Agent called from for outbound calls. + + + + + Filter calls by the number they were dispatched to. + +The number that answered the call - the user's phone number for outbound calls, or your AI Agent's number for inbound calls. + + + + + The starting index (inclusive) for the range of calls to retrieve. + + + + The ending index for the range of calls to retrieve. + + + + The maximum number of calls to return in the response. + + + + Whether to sort the calls in ascending order of their creation time. + + +### Response + + + The total number of calls returned in the response. + + + + An array of call data objects. See the [Call](/api-v1/get/calls-id) section for details. + +Note: Individual call transcripts are not included due to their size. + + + + +```json Response +{ + "count": 784, + "calls": [ + { + "call_id": "c1234567-89ab-cdef-0123-456789abcdef", + "created_at": "2023-12-21T23:25:14.801193+00:00", + "call_length": 0.834, // minutes + "to": "5551234567", + "from": "+15551234567", + "completed": true, + "queue_status": "complete", + "error_message": null, + "answered_by": "human", + "batch_id": "b1234567-89ab-cdef-0123-gen-batch", + }, + //... Additional call objects + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/inbound-number.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/inbound-number.mdx new file mode 100644 index 00000000000..3e7fde1e44a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/inbound-number.mdx @@ -0,0 +1,78 @@ +--- +title: Inbound Number Details +subtitle: Retrieve settings for your inbound phone number. +slug: api-v1/get/inbound-number +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The inbound phone number to update. + + Formatting notes: + - The `'+'` or `'%2B'` prefix is optional. + - Will assume a US country code if no country code is provided. + + Valid Examples for `+13334445555`: + - `%2B13334445555` + - `13334445555` + - `3334445555` + + + +### Response + + + The timestamp when the inbound number was configured. + + + + The specific inbound phone number. + + + + The prompt your agent is using. + + + + The webhook URL, if any, where transcripts are sent after each call to the number completes. + + + + The `voice` your agent is using - will be a `reduce_latency` voice. For more information, see [List + Voices](/api-v1/get/voices). + + + + The `pathway_id` your agent is using. + + + + + Any dynamic data associated with the inbound number, if applicable. + + + + The maximum duration of a call to the inbound number, in minutes. + + + +```json Response +{ + "created_at": "2023-11-27T17:21:38.33359+00:00", + "phone_number": "+18584139939", + "prompt": "You're Blandie, the helpful AI assistant. The person calling you has inquired...", + "webhook": "https://webhook.site/0a0a0a0a-0a0a-0a0a-0a0a-0a0a0a0a0a0", + "voice_id": 2, + "dynamic_data": null, + "max_duration": 30 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/inbound.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/inbound.mdx new file mode 100644 index 00000000000..eb9928a8cda --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/inbound.mdx @@ -0,0 +1,74 @@ +--- +title: List Inbound Numbers +subtitle: >- + Retrieves a list of all inbound phone numbers configured for your account, + along with their associated settings. +slug: api-v1/get/inbound +--- + + +### Headers + + + Your API key for authentication. + + + + Use your own Twilio account and only return inbound numbers associated with that account sid (optional). +{" "} + +### Response + + + An array of objects, each representing an inbound phone number and its configuration. + + + +```json Response +{ + "inbound_numbers": [ + { + "created_at": "2023-11-27T17:21:38.33359+00:00", + "phone_number": "+18005551234", + "prompt": "When you receive a call, recite a random poem from 'Sunset Boulevard' and then ask, 'How may I assist you in your poetic journey today?'", + "webhook": "https://api.example.com/poetry-line", + "voice_id": 2, + "dynamic_data": [/* Use [Dynamic Data](/api-reference/endpoint/dynamic_validate) to make API requests mid-call */], + "interruption_threshold": null, + "first_sentence": null, + "reduce_latency": true, + "transfer_phone_number": null, + "voice_settings": null, + "record": false, + "max_duration": 30 + }, + { + "created_at": "2023-11-25T21:42:22.325993+00:00", + "phone_number": "+18005559876", + "prompt": "Answer with 'You've reached the Secret Society of Enigmatic Enthusiasts. Please state the password or leave a message after the beep.'", + "webhook": "https://mysteryclub.example.com/inbound-call-hook", + "voice_id": 1, + "dynamic_data": null, + "interruption_threshold": null, + "first_sentence": null, + "reduce_latency": true, + "transfer_phone_number": null, + "voice_settings": null, + "record": false, + "max_duration": 30 + }, + //... + ] +} + +``` + + + + + + + + + +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/me.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/me.mdx new file mode 100644 index 00000000000..29b84a897ff --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/me.mdx @@ -0,0 +1,40 @@ +--- +title: Account Details +subtitle: Returns call data for your account. +slug: api-v1/get/me +--- + + +### Headers + + + Your API key for authentication. + + +### Response + + + An object containing your billing data. Contains `current_balance` (number of credits), and `refill_to` if you have + auto refill enabled. + + + + The status of your account. + + + + The total number of calls you've made. + + + +```json Response +{ + "status": "active", + "billing": { + "current_balance": 99919.1210000034, + "refill_to": null + }, + "total_calls": 9903 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/outbound.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/outbound.mdx new file mode 100644 index 00000000000..c50faeb9d01 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/outbound.mdx @@ -0,0 +1,37 @@ +--- +title: List Outbound Numbers +subtitle: >- + Retrieves a list of all outbound phone numbers configured for your account, + along with their associated settings. +slug: api-v1/get/outbound +--- + + +### Headers + + + Your API key for authentication. + + +### Response + + + An array of outbound phone number objects. + + + + +```json Response +{ + "outbound_numbers": [ + { + "created_at": "2023-11-27T17:21:38.33359+00:00", + "phone_number": "+18005551234", + "last_initiated": "2023-12-08T21:47:49.808+00:00" + } + //... + ] +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/subaccounts-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/subaccounts-id.mdx new file mode 100644 index 00000000000..3b75474647c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/subaccounts-id.mdx @@ -0,0 +1,47 @@ +--- +title: List Subaccount Details +subtitle: View a subaccount's details. +slug: api-v1/get/subaccounts-id +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the subaccount. + + +### Response + + + Whether the subaccount creation succeeded. + + + + The affected subaccount's unique identifier. + + + + The new API key for the subaccount. + + + +```json +{ + "status": "success", + "subaccount": { + "subaccount_id": "1234567890", + "first_name": "John", + "last_name": "Doe", + "balance": 150, + "api_key": "sub-sk-1234567890" + } +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/subaccounts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/subaccounts.mdx new file mode 100644 index 00000000000..133586caf52 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/subaccounts.mdx @@ -0,0 +1,46 @@ +--- +title: List Subaccounts +subtitle: View all your subaccounts and their details. +slug: api-v1/get/subaccounts +--- + + +### Headers + + + Your API key for authentication. + + +### Response + + + Whether the subaccount creation succeeded. + + + + An array of your subaccounts. + + + +```json +{ + "status": "success", + "subaccounts": [ + { + "subaccount_id": "1234567890", + "first_name": "John", + "last_name": "Doe", + "balance": 150, + "api_key": "sub-sk-1234567890" + }, + { + "subaccount_id": "0987654321", + "first_name": "Jane", + "last_name": "Doe", + "balance": 200, + "api_key": "sub-sk-0987654321" + } + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/tools-tool-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/tools-tool-id.mdx new file mode 100644 index 00000000000..1c52c180aa3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/tools-tool-id.mdx @@ -0,0 +1,70 @@ +--- +title: Custom Tool Details +subtitle: Retrieve a Custom Tool you've created. +slug: api-v1/get/tools-tool-id +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The ID of the tool you want to retrieve (starting with `TL-`). + + +### Response + + + Whether the requet succeeded or failed. + + + + The tool you've created. + + + +```json Response +{ + "status": "success", + "tool": { + "tool_id": "TL-5da8347d-0ab7-415d-b156-a7fc5c6074dc", + "label": null, + "tool": { + "name": "BookAppointment", + "description": "Books the appointment. Can only be used once.", + "speech": "Please wait while I book that appointment for you", + "method": "POST", + "timeout": 99999999, + "url": "https://...", + "body": { + "slot": "{{input}}" + }, + "input_schema": { + "type": "object", + "example": { + "date": "2024-03-16", + "time": "5:00 PM" + }, + "required": [ + "date", + "time" + ], + "properties": { + "date": "YYYY-MM-DD", + "time": "HH:MM (AM|PM)" + } + }, + "response": { + "confirmation_message": "$.message" + } + }, + "public": false + } +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/tools.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/tools.mdx new file mode 100644 index 00000000000..8f9fd57df11 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/tools.mdx @@ -0,0 +1,67 @@ +--- +title: List Custom Tools +subtitle: Retrieve Custom Tools you've created. +slug: api-v1/get/tools +--- + + +### Headers + + + Your API key for authentication. + + +### Response + + + Whether the requet succeeded or failed. + + + + An array of your available tools. + + + +```json Response +{ + "status": "success", + "tools": [ + { + "tool_id": "TL-5da8347d-0ab7-415d-b156-a7fc5c6074dc", + "label": null, + "tool": { + "name": "BookAppointment", + "description": "Books the appointment. Can only be used once.", + "speech": "Please wait while I book that appointment for you", + "method": "POST", + "timeout": 99999999, + "url": "https://...", + "body": { + "slot": "{{input}}" + }, + "input_schema": { + "type": "object", + "example": { + "date": "2024-03-16", + "time": "5:00 PM" + }, + "required": [ + "date", + "time" + ], + "properties": { + "date": "YYYY-MM-DD", + "time": "HH:MM (AM|PM)" + } + }, + "response": { + "confirmation_message": "$.message" + } + }, + "public": false + }, + ... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/voices-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/voices-id.mdx new file mode 100644 index 00000000000..1327268281c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/voices-id.mdx @@ -0,0 +1,61 @@ +--- +title: Voice Details +subtitle: Retrieve detailed information about a specific voice. +slug: api-v1/get/voices-id +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the voice preset. + +Place either the voice's `name` or `id` here. + +For example, `GET https://api.bland.ai/v1/voices/david` or `GET https://api.bland.ai/v1/voices/ff2c405b-3dba-41e0-9261-bc8ee3f91f46`. + + + +### Response + + + Contains detailed information about the specified voice. + + - `id` - The unique id for that voice. + - `name` - Public voice name, and can also be used as a unique identifier. + - `description` - The description of the voice. + - `public` - Whether or not the voice is publicly available. + - `tags` - A list of tags associated with the voice for the language, voice details, and will have `"Bland Curated"` for preferred voices. + + - `average_rating` - The average star ratings for the voice (out of 5 stars). + - `total_ratings` - The number of ratings for the voice. + + Note: Ratings are under development and may show incomplete or inaccurate data. + + + + +```json +{ + "voice": { + "id": "2f9fdbc7-4bf2-4792-8a18-21ce3c93978f", + "name": "maya", + "description": "Young American Female", + "public": true, + "tags": [ + "english", + "soft", + "Bland Curated" + ], + "total_ratings": 1234, + "average_rating": 4 + } +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/voices.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/voices.mdx new file mode 100644 index 00000000000..6c815dcfb9f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/get/voices.mdx @@ -0,0 +1,100 @@ +--- +title: List Voices +subtitle: Retrieves all available voices for your account. +slug: api-v1/get/voices +--- + + +### Headers + + + Your API key for authentication. + + +### Response + + + Contains a list of the voices available for your account. + + + The unique identifier for the voice. + + + + The name of the voice. This value can also be used in the `voice` parameter when sending calls. + + + + A brief description of the voice. + + + + Indicates whether the voice is publicly available or specific to your account. + + + + A list of tags that describe the voice. We recommend "Bland Curated" voices for the best quality over the phone. + + + + The number of ratings the voice has received. + + + + The average rating of the voice, out of 5. + + + Note: Ratings are under development at this time and may display incomplete/inaccurate data. + + + + +```json +{ + "voices": [ + { + "id": "2f9fdbc7-4bf2-4792-8a18-21ce3c93978f", + "name": "maya", + "description": "Young American Female", + "public": true, + "tags": [ + "english", + "soft", + "Bland Curated" + ] + }, + { + "id": "37b3f1c8-a01e-4d70-b251-294733f08371", + "name": "ryan", + "description": "Professional American Male", + "public": true, + "tags": [ + "english", + "Bland Curated" + ] + }, + { + "id": "90295ec4-f0fe-4783-ab33-8b997ddc3ae4", + "name": "mason", + "description": "American Male", + "public": true, + "tags": [ + "english", + "Bland Curated" + ] + }, + { + "id": "bbeabae6-ec8d-444f-92ad-c8e620d3de8d", + "name": "tina", + "description": "Gentle American Female", + "public": true, + "tags": [ + "english", + "gentle" + ] + }, + //... + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/accounts-delete.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/accounts-delete.mdx new file mode 100644 index 00000000000..02a1985863a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/accounts-delete.mdx @@ -0,0 +1,42 @@ +--- +title: Delete Encrypted Key +subtitle: >- + Disable an encrypted key for a Twilio account integration. See [Enterprise + Twilio Integration](/enterprise-features/custom-twilio) for more information. +slug: api-v1/post/accounts-delete +--- + + +### Headers + + + Your API key for authentication. + + + + The `encrypted_key` to delete. + + +### Response + + + The status of the request. + + - `success` - The encrypted key was successfully deleted. + - `error` - There was an error deleting the encrypted key. + + + + + Special messages: - `Error deleting Twilio credentials` - The encrypted key could not be deleted or already has been + deleted. - `Missing encrypted key` - The `encrypted_key` parameter is missing. - none - The encrypted key was + successfully deleted. + + + +```json +{ + "status": "success" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/accounts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/accounts.mdx new file mode 100644 index 00000000000..4c410d20abc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/accounts.mdx @@ -0,0 +1,39 @@ +--- +title: Create Encrypted Key +subtitle: >- + Integrate your own Twilio account with Bland. See [Enterprise Twilio + Integration](/enterprise-features/custom-twilio) for more information. +slug: api-v1/post/accounts +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + Your Twilio account SID. + + + + Your Twilio auth token. + + +### Response + + + Your `encrypted_key` to store and use in future requests. + + + +```json +{ + "status": "success", + "encrypted_key": "YOUR_ENCRYPTED_KEY" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id-authorize.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id-authorize.mdx new file mode 100644 index 00000000000..b010a174744 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id-authorize.mdx @@ -0,0 +1,62 @@ +--- +title: Authorize a Web Agent Call +subtitle: Create a single-use session token for a client to talk with your web agent. +slug: api-v1/post/agents-id-authorize +--- + + +### Headers + + + Your API key for authentication. + +Example web call usage (client side): + +```javascript +import { BlandWebClient } from "bland-client-js-sdk"; + +const agentId = "YOUR-AGENT-ID"; +const sessionToken = "YOUR-SESSION-TOKEN"; + +document.addEventListener("DOMContentLoaded", async () => { + document.getElementById("btn").addEventListener("click", async () => { + const blandClient = new BlandWebClient(agentId, sessionToken); + await blandClient.initConversation({ + sampleRate: 44100, + }); + }); +}); +``` + + + +### Path + + + The web agent to authorize a call for. + +Special note: While in Beta, this request must be made to the `web.bland.ai` domain. + + + +### Response + + + The single-use session token that can be sent to the client. + + + + Can be `success` or `error`. + + + + A message saying whether the token creation succeeded, or a helpful message describing why it failed. + + + +```json +{ + "token": "22480c52-0ff1-4214-bcb7-50649b508432" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id-delete.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id-delete.mdx new file mode 100644 index 00000000000..cacef4cdee8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id-delete.mdx @@ -0,0 +1,37 @@ +--- +title: Delete Web Agent +subtitle: Delete a web agent. +slug: api-v1/post/agents-id-delete +--- + + +### Headers + + + Your API key for authentication. + + +### Path + + + The web agent to delete. + + +### Response + + + Can be `success` or `error`. + + + + A message saying whether the deletion succeeded, or a helpful message describing why it failed. + + + +```json +{ + "status": "success", + "message": "Successfully deleted agent 2c565dc7-f41f-43db-a99f-e4c8ba9d7d18" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id.mdx new file mode 100644 index 00000000000..f9ef710ffbe --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents-id.mdx @@ -0,0 +1,211 @@ +--- +title: Update Web Agent Settings +subtitle: Update your web agent's settings, prompt and other details. +slug: api-v1/post/agents-id +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The web agent you'll be updating. + + +### Body + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + + + Set your agent's voice - all available voices can be found with the [List Voices](/api-v1/get/voices) endpoint. + + + + Define a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data. + +In the webhook response or whenever you retrieve call data later, you'll get the data you defined back under `analysis`. + +For example, if you wanted to retrieve this information from the call: + +```json +"analysis_schema": { + "email_address": "email", + "first_name": "string", + "last_name": "string", + "wants_to_book_appointment": "boolean", + "appointment_time": "YYYY-MM-DD HH:MM:SS" +} +``` + +You would get it filled out like this in your webhook once the call completes: + +```json +"analysis": { + "email_address": "johndoe@gmail.com", + "first_name": "John", + "last_name": "Doe", + "wants_to_book_appointment": true, + "appointment_time": "2024-01-01 12:00:00" +} +``` + + + + + Add any additional information you want to associate with the call. This can be useful for tracking or categorizing + calls. + + + + Set the pathway that your agent will follow. This will override the `prompt` field, so there is no need to pass the 'prompt' field if you are setting a pathway. + + Warning: Setting a pathway will set the following fields to `null` / their default value - `prompt`, `first_sentence`, `model`, `dynamic_data`, `tools` + + Set to `null` or an empty string to clear the pathway. + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```ENG``` - Spanish: ```ESP``` - French: + ```FRE``` - Polish: ```POL``` - German: ```GER``` - Italian: ```ITA``` - Brazilian Portuguese: ```PBR``` - Portuguese: + ```POR``` + + + + The webhook should be a http / https callback url. We will send the call_id + and transcript to this URL after the call completes. This can be useful if you + want to have real time notifications when calls finish. + +Set to `null` or an empty string to clear the webhook. + + + + + Select a model to use for your call. + +Options: `base`, `turbo` and `enhanced`. + +In nearly all cases, `enhanced` is the best choice for now. + + + +There are three different ways to use Bland: + +- `model: base` + + - The original, follows scripts/procedures most effectively. + - Supports all features and capabilities. + - Best for Custom Tools + +- `model: enhanced` + + - Much faster latency and very conversational, works best with objective-based prompts. + - Supports all features and capabilities. + +- `model: turbo` + + - The absolute fastest latency possible, can be verbose at times + - Limited capabilities currently (excludes Transferring, IVR navigation, Custom Tools) + - Extremely realistic conversation capabilities + + + + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + +To remove, set to `null` or an empty string. + + + + + Interact with the real world through API calls. + +Detailed tutorial here: [Custom Tools](/tutorials/custom-tools) + + + + + Integrate data from external APIs into your agent's knowledge. + +Set to `null` or an empty string to clear dynamic data settings. + +Detailed usage in the [Send Call](/api-v1/post/calls) endpoint. + + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `500` milliseconds. You'll encounter higher latency, but you'll be interrupted less frequently. + +Set to `null` to reset to default. + + + + + The maximum duration that calls to your agent can last before being automatically terminated. + +Set to `null` to reset to default. + + + +### Response + + + Whether the update was successful or not - will be `success` or `error`. + + + + A message describing the status of the update. + + + + An object containing the updated settings for the agent. + + + + If the update was unsuccessful, this will contain the settings that failed to update. Useful to determine how your + request is being interpreted on our end. + + + + +```json Response +{ + "status": "success", + "message": "Successfully updated agent 46f37229-7d12-44be-b343-6e68274cfbea.", + "updates": { + "model": "enhanced" + } +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents.mdx new file mode 100644 index 00000000000..373b73b02da --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/agents.mdx @@ -0,0 +1,216 @@ +--- +title: Create a Web Agent +subtitle: Configure all of the settings for a new web agent. +slug: api-v1/post/agents +--- + + +### Headers + + + Your API key for authentication. + +Example web call usage (client side): + +```javascript +import { BlandWebClient } from "bland-client-js-sdk"; + +const agentId = "YOUR-AGENT-ID"; +const sessionToken = "YOUR-SESSION-TOKEN"; + +document.addEventListener("DOMContentLoaded", async () => { + document.getElementById("btn").addEventListener("click", async () => { + const blandClient = new BlandWebClient(agentId, sessionToken); + await blandClient.initConversation({ + sampleRate: 44100, + }); + }); +}); +``` + + +### Body + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + - Ends call when objective is complete or voicemail is detected + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + + + Set your agent's voice - all available voices can be found with the [List Voices](/api-v1/get/voices) endpoint. + + + + Define a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data. + +In the webhook response or whenever you retrieve call data later, you'll get the data you defined back under `analysis`. + +For example, if you wanted to retrieve this information from the call: + +```json +"analysis_schema": { + "email_address": "email", + "first_name": "string", + "last_name": "string", + "wants_to_book_appointment": "boolean", + "appointment_time": "YYYY-MM-DD HH:MM:SS" +} +``` + +You would get it filled out like this in your webhook once the call completes: + +```json +"analysis": { + "email_address": "johndoe@gmail.com", + "first_name": "John", + "last_name": "Doe", + "wants_to_book_appointment": true, + "appointment_time": "2024-01-01 12:00:00" +} +``` + + + + + Add any additional information you want to associate with the call. This can be useful for tracking or categorizing + calls. + + + + Set the pathway that your agent will follow. This will override the `prompt` field, so there is no need to pass the 'prompt' field if you are setting a pathway. + + Warning: Setting a pathway will set the following fields to `null` / their default value - `prompt`, `first_sentence`, `model`, `dynamic_data`, `tools` + + Set to `null` or an empty string to clear the pathway. + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```ENG``` - Spanish: ```ESP``` - French: + ```FRE``` - Polish: ```POL``` - German: ```GER``` - Italian: ```ITA``` - Brazilian Portuguese: ```PBR``` - Portuguese: + ```POR``` + + + + Select a model to use for your call. + +Options: `base`, `turbo` and `enhanced`. + +In nearly all cases, `enhanced` is the best choice for now. + + + +There are three different ways to use Bland: + +- `model: base` + + - The original, follows scripts/procedures most effectively. + - Supports all features and capabilities. + - Best for Custom Tools + +- `model: enhanced` + + - Much faster latency and very conversational, works best with objective-based prompts. + - Supports all features and capabilities. + +- `model: turbo` + + - The absolute fastest latency possible, can be verbose at times + - Limited capabilities currently (excludes Transferring, IVR navigation, Custom Tools) + - Extremely realistic conversation capabilities + + + + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + +To remove, set to `null` or an empty string. + + + + + Interact with the real world through API calls. + +Detailed tutorial here: [Custom Tools](/tutorials/custom-tools) + + + + + Integrate data from external APIs into your agent's knowledge. + +Set to `null` or an empty string to clear dynamic data settings. + +Detailed usage in the [Send Call](/api-v1/post/calls) endpoint. + + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `500` milliseconds. You'll encounter higher latency, but you'll be interrupted less frequently. + +Set to `null` to reset to default. + + + + + The maximum duration that calls to your agent can last before being automatically terminated. + +Set to `null` to reset to default. + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + +```json Response +{ + "agent": { + "agent_id": "2c565dc7-f41f-43db-a99f-e4c8ba9d7d18", + "webhook": null, + "dynamic_data": null, + "interruption_threshold": null, + "first_sentence": null, + "model": "enhanced", + "voice_settings": null, + "voice": "maya", + "prompt": "...", + "temperature": null, + "max_duration": 30, + "language": "ENG", + "tools": null + } +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches-id-analyze.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches-id-analyze.mdx new file mode 100644 index 00000000000..8e3960204a9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches-id-analyze.mdx @@ -0,0 +1,94 @@ +--- +title: Analyze Batch with AI +subtitle: Analyzes a batch of calls based on using questions and goals. +slug: api-v1/post/batches-id-analyze +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the batch of calls to be analyzed. + + +### Request Body + + + This is the overall purpose of the batch of calls. Provides context for the analysis to guide how the + questions/transcripts are interpreted. + + + + An array of questions to be analyzed for each call in the batch. + + Each question should be an array with two elements: the question text and the expected answer type (e.g., "string", "boolean"). + +Fairly flexible in terms of the expected answer type, and unanswerable questions will default to `null`. + +Examples: + +```json +"questions": [ + ["Who answered the call?", "human or voicemail"], + ["Positive feedback about the product: ", "string"], + ["Negative feedback about the product: ", "string"], + ["Customer confirmed they were satisfied", "boolean"] + ] +``` + + + +### Response + + + Will be `success` if the request was successful. + + + + Confirms the request was successful, or provides an error message if the request failed. + + + + Contains the analyzed answers for each call in the batch. + +The keys are `call_id`s from the batch, and the array values are the analysis results for each question in the batch. + + + + + Token-based price for the analysis request. + +As a rough estimate, the base cost is `0.003` credits with an additional `0.0015` credits per call in the batch. + +Longer call transcripts and higher numbers of questions can increase the cost, however the cost scales very effectively with batches vs. individual calls. + + + + +```json +{ + "status": "success", + "message": "Successfully analyzed batch", + "answers": { + "123e4567-e89b-12d3-a456-426614174000": [ + "human", + "Customer found the product sturdy and reliable", + "A bit heavy", + true + ], + "123e4567-e89b-12d3-a456-426614174001": [ + "voicemail", + null, + null, + false + ] + } +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches-id-stop.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches-id-stop.mdx new file mode 100644 index 00000000000..6e6a9105448 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches-id-stop.mdx @@ -0,0 +1,50 @@ +--- +title: Stop Active Batch +subtitle: Stops all active calls in a batch. +slug: api-v1/post/batches-id-stop +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the batch to be cancelled. + + +### Response + + + The status of the request. If anything other than 'success', an error has occurred or all calls have already been + completed. + + + + A message describing the status of the request. + + + + The number of calls that were cancelled. + + + + The `batch_id` of the cancelled batch. + + + + +```json Response +{ + "status": "success", + "message": "Successfully stopped batch", + "num_calls": 1205, + "batch_id": "5e5b1a3a-2b0a-4b9e-8b9e-asdf-gen-batch" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches.mdx new file mode 100644 index 00000000000..739eeecef9c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/batches.mdx @@ -0,0 +1,92 @@ +--- +title: Send a Batch of Calls +subtitle: Send large volumes of calls at once with a single API request. +slug: api-v1/post/batches +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + This is the prompt or task used for all the phone calls in the request. Information can be inserted into it surrounding variable names with \{\{curly braces\}\}. + +Example: + +```json +"You are calling {{business}} to renew their subscription to {{service}} before it expires on {{date}}." +``` + + + + + Define a list of calls to make and their properties. Each call in call_data MUST have a "phone_number" property. Properties are case-sensitive. + +Example: + +```json +[ + { + "phone_number": "1234567890", + "business": "ABC Corp", + "service": "Netflix", + "date": "September 4th" + }, + { + "phone_number": "32176540987", + "business": "XYZ inc.", + "service": "Window Cleaning", + "date": "December 20th" + } +] +``` + + + + + Adds a user-friendly label to your batch to keep track of it's original intention. This can help differentiate + multiple call batches that are part of the same Campaign. Shown when a batch is retreived. + + + + Use ```campaign_id``` to organize related batches together. This can be set manually or auto-generated through + Campaigns. + + + + When this is set to ```true```, only the first call of ```call_data``` will be dispatched. A common use case is to set the first ```phone_number``` value to your own to confirm everything's set up properly. + +Includes additional information in the response when true so that it's easier to find any issues. + + + + + All other parameters supported by the [Send Call](/api-v1/post/calls) endpoint are supported here as well. They will + be applied to each call in the batch. + + +### Response + + + If anything other than "success" is returned, there was an error. + + + + The unique identifier for the batch. + + + + +```json Response +{ + "message": "success", + "batch_id": "3p$7rQ3p9sT5bzmF-gen-batch" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-id-analyze.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-id-analyze.mdx new file mode 100644 index 00000000000..4ec5196a784 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-id-analyze.mdx @@ -0,0 +1,83 @@ +--- +title: Analyze Call with AI +subtitle: Analyzes a call of calls based using questions and goals. +slug: api-v1/post/calls-id-analyze +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the call to be analyzed. + + +### Request Body + + + This is the overall purpose of the call. Provides context for the analysis to guide how the questions/transcripts are + interpreted. + + + + An array of questions to be analyzed for the call. + + Each question should be an array with two elements: the question text and the expected answer type (e.g., "string", "boolean"). + +Fairly flexible in terms of the expected answer type, and unanswerable questions will default to `null`. + +Examples: + +```json +"questions": [ + ["Who answered the call?", "human or voicemail"], + ["Positive feedback about the product: ", "string"], + ["Negative feedback about the product: ", "string"], + ["Customer confirmed they were satisfied", "boolean"] + ] +``` + + + +### Response + + + Will be `success` if the request was successful. + + + + Confirms the request was successful, or provides an error message if the request failed. + + + + Contains the analyzed answers for the call in an array. + + + + Token-based price for the analysis request. + +As a rough estimate, the base cost is `0.003` credits with an additional `0.0015` credits per call in the call. + +Longer call transcripts and higher numbers of questions can increase the cost, however the cost scales very effectively with calls vs. individual calls. + + + + +```json +{ + "status": "success", + "message": "Successfully analyzed call", + "answers": [ + "human", + "Customer found the product sturdy and reliable", + "A bit heavy", + true + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-id-stop.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-id-stop.mdx new file mode 100644 index 00000000000..ab21194bc9f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-id-stop.mdx @@ -0,0 +1,38 @@ +--- +title: Stop Active Call +subtitle: End an active phone call by call_id. +slug: api-v1/post/calls-id-stop +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier for the call you want to end. + + +### Response + + + Can be `success` or `error`. + + + + If the status is `success`, the message will say "Call ended successfully." Otherwise, if the status is `error`, the + message will say "SID not found for the given c_id." or "Internal server error." + + + +```json +{ + "status": "success", + "message": "Call ended successfully." +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-simple-pathway.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-simple-pathway.mdx new file mode 100644 index 00000000000..0776b814393 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-simple-pathway.mdx @@ -0,0 +1,79 @@ +--- +title: Send Call using Pathways (Simple) +subtitle: >- + Send an AI phone call with your own conversational pathway agent! Links - + [Video + Tutorial](https://www.loom.com/share/5ce5a84ec97149efad7cf5eff66a93c5?sid=697dc436-53cf-494c-a3e9-a25031df6496) + | [Step-by-step web tutorial](https://docs.bland.ai/tutorials/pathways) +slug: api-v1/post/calls-simple-pathway +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to call. Country code defaults to `+1` (US) if not specified. + + Formatting is flexible, however for the most predictable results use the [E.164](https://www.twilio.com/docs/glossary/what-e164#examples-of-e164-numbers) format. + + + Expected/Ideal Format: + - "+12223334444" + - "+91223334444" + - "+61223334444" + + Valid, but not recommended: + - "2223334444" + - "+1 (222) 333-4444" + - "+1 222 333 4444" + - "222-333-4444" + + Invalid: + - "12223334444" + - "552223334444" + - "non-numeric characters" + - "2223334444 ext. 123" + + + + + + Follows the conversational pathway you created to guide the conversation. + +You can access your pathway_id by clicking on the 'Copy ID' button on your pathways [here](https://app.bland.ai/home?page=convo-pathways). If you don't have any pathways, click the 'Create Pathway' button to create one! + + + + [Video tutorial](https://www.loom.com/share/5ce5a84ec97149efad7cf5eff66a93c5?sid=697dc436-53cf-494c-a3e9-a25031df6496) + + [Step by step Web Tutorial](https://docs.bland.ai/tutorials/pathways) + + + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-simple.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-simple.mdx new file mode 100644 index 00000000000..9d4ae9888d2 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls-simple.mdx @@ -0,0 +1,83 @@ +--- +title: Send Call (Simple) +subtitle: Send an AI phone call with a custom objective and actions. +slug: api-v1/post/calls-simple +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to call. Country code defaults to `+1` (US) if not specified. + + Formatting is flexible, however for the most predictable results use the [E.164](https://www.twilio.com/docs/glossary/what-e164#examples-of-e164-numbers) format. + + + Expected/Ideal Format: + - "+12223334444" + - "+91223334444" + - "+61223334444" + + Valid, but not recommended: + - "2223334444" + - "+1 (222) 333-4444" + - "+1 222 333 4444" + - "222-333-4444" + + Invalid: + - "12223334444" + - "552223334444" + - "non-numeric characters" + - "2223334444 ext. 123" + + + + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + - Ends call when objective is complete or voicemail is detected + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + +```json Response +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls.mdx new file mode 100644 index 00000000000..4d935f69bd0 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/calls.mdx @@ -0,0 +1,550 @@ +--- +title: Send Call +subtitle: Send an AI phone call with a custom objective and actions. +slug: api-v1/post/calls +--- + + +### Headers + + + Your API key for authentication. + + + + A special key for using a BYOT (Bring Your Own Twilio) account. Not required in most cases. + + +### Body + + + The phone number to call. Country code defaults to `+1` (US) if not specified. + + Formatting is flexible, however for the most predictable results use the [E.164](https://www.twilio.com/docs/glossary/what-e164#examples-of-e164-numbers) format. + + + Expected/Ideal Format: + - "+12223334444" + - "+91223334444" + - "+61223334444" + + Valid, but not recommended: + - "2223334444" + - "+1 (222) 333-4444" + - "+1 222 333 4444" + - "222-333-4444" + + Invalid: + - "12223334444" + - "552223334444" + - "non-numeric characters" + - "2223334444 ext. 123" + + + + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + - Ends call when objective is complete or voicemail is detected + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + + + The voice of the AI agent to use. Accepts any form of voice ID, including custom voice clones and voice presets. + +Default voices can be referenced directly by their name instead of an id. + +Usage example: `voice: "maya"` + +Bland Curated voices: + +- `maya` +- `mason` +- `ryan` +- `adriana` +- `tina` +- `matt` +- `evelyn` + +Use the [GET /v1/voices](https://api.bland.ai/voices) endpoint to see a full list of your available voices. + + + + Note: Including `voice_id` or `reduce_latency` in your request is still supported, but not recommended. + + The previous structure to select voices used both `voice_id` and `reduce_latency`. To simplify the process, we've combined these into a single `voice` parameter. + + - If the first two letters of `voice` are `RL`, that is equivalent to settings `reduce_latency` to `true`. + - Prefixing the voice ID with `HQ` will use the high fidelity version of the voice. + - The integer following the prefix is the `voice_id` from before. + + Example: + - `reduce_latency: true, voice_id: 0` is equivalent to `voice: "RL0"` + - `reduce_latency: false, voice_id: 3` is equivalent to `voice: "HQ3"` + + Including `reduce_latency` may override the `voice` parameter, so exclude it when using `voice`. + + + + + All presets have been migrated to the `voice` parameter and can use either the preset name or ID. + + If you used to have a `voice_preset_id` of `"2f9fdbc7-4bf2-4792-8a18-21ce3c93978f"`, you can now use `voice: "2f9fdbc7-4bf2-4792-8a18-21ce3c93978f"`. + + + + + + Define a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data. + +In the webhook response or whenever you retrieve call data later, you'll get the data you defined back under `analysis`. + +For example, if you wanted to retrieve this information from the call: + +```json +"analysis_schema": { + "email_address": "email", + "first_name": "string", + "last_name": "string", + "wants_to_book_appointment": "boolean", + "appointment_time": "YYYY-MM-DD HH:MM:SS" +} +``` + +You would get it filled out like this in your webhook once the call completes: + +```json +"analysis": { + "email_address": "johndoe@gmail.com", + "first_name": "John", + "last_name": "Doe", + "wants_to_book_appointment": true, + "appointment_time": "2024-01-01 12:00:00" +} +``` + + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without + `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + + + + Should the AI speak first or wait for someone else to talk? + + Creates more realistic conversations when answered with "Hello?", "This is \{name\} speaking." and so on. + + - When ```false```: The AI starts speaking shortly after the call is answered. + + - When ```true```: The AI will wait for the answerer to speak. + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `200` milliseconds. You'll encounter higher latency, but you'll be interrupted much less frequently. + + + + + This is the pathway ID for the pathway you have created on our dev portal. You + can access the ID of your pathways by clicking the 'Copy ID' button of your + pathway [here](https://app.bland.ai/home?page=convo-pathways) + +Note: Certain parameters do not apply when using pathways. + +{" "} + + + - `task` - The pathway substitutes as the agent's instructions. - `model` - We use our own fine-tuned models under the + hood. - `tools` - Replaced by 'Webhook' Node in Pathways - `transfer_list` - Replaced by 'Transfer Call' Node in + Pathways - `transfer_phone_number` - Replaced by 'Transfer Call' Node in Pathways + + +Example Simple Request body: + +```json +"phone_number": "+1975934749", +"pathway_id": "a0f0d4ed-f5f5-4f16-b3f9-22166594d7a7" +``` + + + + + Select a model to use for your call. + +Options: `gpt4`, `base`, `turbo` and `enhanced`. + +In nearly all cases, `enhanced` is the best choice for now. + + + +There are four different ways to use Bland: + +- `model: gpt4` + + - Slow but accurate + - Supports all features and capabilities. + - Best for complex tasks where latency isn't a priority + +- `model: base` + + - The original, follows scripts/procedures most effectively. + - Supports all features and capabilities. + - Best for Custom Tools + +- `model: enhanced` + + - Much faster latency and very conversational, works best with objective-based prompts. + - Supports all features and capabilities. + +- `model: turbo` + + - The absolute fastest latency possible, can be verbose at times + - Limited capabilities currently (excludes Transferring, IVR navigation, Custom Tools) + - Extremely realistic conversation capabilities + + + + + + + Interact with the real world through API calls. + +Detailed tutorial here: [Custom Tools](/tutorials/custom-tools) + + + + + Add any additional information you want to associate with the call. This can be useful for tracking or categorizing calls. + +Anything that you put here will be returned in your webhook or in the call details under `metadata`. + +Example: + +```json +"metadata": { + "campaign_id": "1234", + "source": "web" +} +``` + + + + + The webhook should be a http / https callback url. We will send the call_id and transcript to this URL after the call + completes. This can be useful if you want to have real time notifications when calls finish. + + + + To record your phone call, set `record` to true. When your call completes, you can access through the `recording_url` + field in the call details or your webhook. + + + + A phone number that the agent can transfer to under specific conditions - such as being asked to speak to a human or supervisor. + + + For best results: + - Specify conditions that the agent should transfer to a human under (examples are great!) + - In the `task`, refer to the action solely as "transfer" or "transferring". + - Alternate phrasing such as "swap" or "switch" can mislead the agent, causing the action to be ignored. + + + + + + Give your agent the ability to transfer calls to a set of phone numbers. + +Overrides `transfer_phone_number` if a `transfer_list.default` is specified. + +Will default to `transfer_list.default`, or the chosen phone number. + +Example usage to route calls to different departments: + +```json +"transfer_list": { + "default": "+12223334444", + "sales": "+12223334444", + "support": "+12223334444", + "billing": "+12223334444" +} +``` + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```ENG``` - Spanish: ```ESP``` - French: + ```FRE``` - Polish: ```POL``` - German: ```GER``` - Italian: ```ITA``` - Brazilian Portuguese: ```PBR``` - Portuguese: + ```POR``` + + + + Set the longest you want the call to possibly go in minutes. After the max_duration minutes have passed, the call will + automatically end. Example Values: ```20, 2``` + + + + Enables machine detection when the call starts to determine whether the call was answered by a person or a voicemail. + +Best Practices (when enabled): + +- Since the determination is made at the beginning of the call, use `wait_for_greeting` to try and coax a human response. +- If combined with `first_sentence`, try wording it so the person answering says something back - ex. `"Hello?"` or `"Is this \{\{name\}\}?"`. + +Price: `$0.02` per call, however there is no charge for unanswered calls or calls that failed to send. + + + + + Specify a purchased Outbound Number to call from. Country code is required, spaces or parentheses must be excluded. + +By default, calls are initiated from a separate pool of numbers owned by Bland. + + + + + The pronunciation guide is an `array` of `objects` that guides the LLM on how to say specific words. This is great for situations with complicated terms or names. + +````json + [ + { + "word": "example", + "pronunciation": "ex-am-ple", + "case_sensitive": "false", + "spaced": "false" + }, + { + "word": "API", + "pronunciation": "A P I", + "case_sensitive": "true", + "spaced": "true" + } + ] + ``` + + + + - `word` + — the word you want to guide the LLM on how to pronounce + - `pronunciation` + — the word you want to guide the LLM on how to pronounce. + - `case_sensitive` + — whether or not to consider case. Particularly useful with names. EG: 'Max' the name versus 'max' the word. Defaults to false. `Not required`. + - `spaced` + — whether or not to consider spaces. When `true` the word 'high' would be flagged but NOT 'hightop'. Defaults to true. `Not required`. + + + + + + A value between 0 and 1 that controls the randomness of the LLM. 0 will cause more deterministic outputs while 1 will cause more random. + + Example Values: ```"0.9", "0.3", "0.5"``` + + + + The time you want the call to start. If you don't specify a time (or the time is in the past), the call will send immediately. + + Set your time in the format `YYYY-MM-DD HH:MM:SS -HH:MM` (ex. `2021-01-01 12:00:00 -05:00`). + + The timezone is optional, and defaults to UTC if not specified. + + Note: Scheduled calls can be cancelled with the [POST /v1/calls/:call_id/stop](/api-v1/post/calls-id-stop) endpoint. + + + + When the AI encounters a voicemail, it will leave this message after the beep and then immediately end the call. + + Warning: If `amd` is set to `true` or `voicemail_action` is set to `ignore`, then this will still work for voicemails, but it will not hang up for IVR systems. + + + + This is processed separately from the AI's decision making, and overrides it. + + Options: + - ```hangup``` + - ```leave_message ``` + - ```ignore``` + + Examples: + - Call is answered by a voicemail (specifically with a beep or tone): + - If `voicemail_message` is set, that message will be left and then the call will end. + - Otherwise, the call immediately ends (regardless of `amd`) + + - Call is answered by an IVR system or phone tree: + - If `amd` is set to `true`, the AI will navigate the system and continue as normal. + - If `voicemail_action` is set to `ignore`, the AI will ignore the IVR and continue as normal. + - Otherwise, if `voicemail_message` is set then it'll leave that message and end the call. + - Finally, if none of those conditions are met, the call will end immediately. + + Note: If `voicemail_message` is set, then the AI will leave the message regardless of the `voicemail_action`. + + + + AMD mode helps your AI navigate phone trees and IVR systems. If you know your call will hit an automated system you should switch it on. + + Behavioral changes: + - Much higher `interruption_threshold` so that the options are listened to in full. + - Underlying prompt is adjusted so the AI is aware it's navigating a phone tree. + + NOTE: AMD mode causes increased delay for the first response, even if answered by a human. Highly recommended to set to `false` in the majority of cases. + + + + When you want your AI to "know" a specific fact - like the caller's + name or other relevant context. + + The AI agent will be aware of both the key names as well as their corresponding values. + + + Example Issue: + - The LLM is hallucinating specific facts. You need to provide specific information. + Example Solution: + - Use `request_data` to specify and label that data. + + ```json + "request_data": { + "first_name":"John", + "date_of_birth":"03/14/05" + // additional parameters as needed + } + ``` + + + + + Make dynamic requests to external APIs and use the data in your AI's responses. + + + Each request object should contain: + + `url`: The URL of the external API to fetch data from. + + `response_data`: An array of objects describing how to parse and use the data fetched from the API. Explained in more detail below. + + The following are optional: + + `method`: Allows `GET` or `POST`. Default: `GET` + + `cache`: Whether to fetch the data once at the beginning of the call, or to re-check continuously for data that might change mid-call. Default: `true` + + `headers`: An object of headers to send with the request. + + `body`: The body of the request. + + The following variables can be injected into the dynamic request body: + + - `{{from}}` (Ex. `+12223334444`) + - `{{to}}` + - `{{short_from}}` (Ex. `2223334444`) + - `{{short_to}}` + - `{{call_id}}` + + These string values will be replaced in each `dynamic_data[].body` where they're used by system values in each request. + + Try out with this example: +```json + "dynamic_data": [ + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json", + "response_data": [ + { + "name": "BTC Price USD", + "data": "bpi.USD.rate", + "context": "Current BTC Price: ${{BTC Price USD}} USD" + }, + { + "name": "BTC Price EUR", + "data": "bpi.EUR.rate", + "context": "In Euros: {{BTC Price USD}} EUR" + } + ] + } + ] +```` + + + An array of objects describing how to parse and use the data fetched from the API. + + Each object in this array should contain: + - `name`: A label for the fetched data. + - Example: `"Flight Status"` + - `data`: The JSON path in the API response to extract the data from. + - Example: `"user.flights[0].status"` + - `context`: How this data should be incorporated into the AI's knowledge. + - Example: `"John's flight is currently {{Flight Status}}"` + + + + + +### Response + + + Can be `success` or `error`. + + + + A unique identifier for the call (present only if status is `success`). + + + + The batch ID of the call (present only if status is `success`). + + + + A message explaining the status of the call. + + + + For validation errors, a detailed list of each field with an error and it's error message. + +Example: + +```json +{ + "status": "error", + "message": "Invalid parameters", + "errors": [ + "Missing required parameter: phone_number.", + "Missing required parameter: task.", + "Phone number must be a string or number.", + "Task must be a string." + ] +} +``` + + + + + +```json Response +{ + "status": "success", + "message": "Call successfully queued.", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1", + "batch_id": null +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-insert.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-insert.mdx new file mode 100644 index 00000000000..e4966bd2323 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-insert.mdx @@ -0,0 +1,59 @@ +--- +title: Upload Inbound Phone Numbers +subtitle: >- + Add inbound numbers to Bland from your own Twilio account. See [Enterprise + Twilio Integration](/enterprise-features/custom-twilio) for more information. +slug: api-v1/post/inbound-insert +--- + + +### Headers + + + Your API key for authentication. + + + + The `encrypted_key` of the Twilio account you want to upload numbers from. + + +### Body + + + An array of phone numbers you want to upload to Bland. + + Include the leading `'+'`, country code and the phone number without any special characters. + + Example: `["+12223334444", "+13334445555"]` + + + +### Response + + + Can be `success` or `error`. + + + + A message saying whether the insertion succeeded, or a helpful message describing why it failed. + + + + An array of phone numbers that were successfully inserted. + +Any phone numbers that failed to be inserted will not be included in this array - for example if they are already in your account or not associated with the sepcified Twilio account. + + + + +```json +{ + "status": "success", + "message": "Successfully inserted numbers", + "inserted": [ + "+12223334444", + "+13334445555" + ] +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-number-delete.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-number-delete.mdx new file mode 100644 index 00000000000..e1ba30fe3ca --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-number-delete.mdx @@ -0,0 +1,44 @@ +--- +title: Delete Inbound Phone Number +subtitle: >- + Remove an inbound number that was uploaded through your own Twilio account. + See [Enterprise Twilio Integration](/enterprise-features/custom-twilio) for + more information. +slug: api-v1/post/inbound-number-delete +--- + + +### Headers + + + Your API key for authentication. + + + + The `encrypted_key` for the Twilio account that owns the phone number you want to delete. + + +### Path + + + The phone number you want to remove from Bland's system. + + +### Response + + + Can be `success` or `error`. + + + + A message saying whether the deletion succeeded, or a helpful message describing why it failed. + + + +```json +{ + "status": "success", + "message": "Successfully deleted number from database: +15555555555" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-number-update.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-number-update.mdx new file mode 100644 index 00000000000..88bb0f71102 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-number-update.mdx @@ -0,0 +1,274 @@ +--- +title: Update Inbound Details +subtitle: Update your inbound agent's settings, prompt and other details. +slug: api-v1/post/inbound-number-update +--- + + +### Headers + + + Your API key for authentication. + + + + The `encrypted_key` for the Twilio account that owns the phone number you want to modify. Not required if you are + using a Bland phone number. + + +### Path Parameters + + + The inbound phone number you wish to update. + + Formatting Notes: + - The `'+'` or `'%2B'` prefix is optional. + - Will assume a US country code if no country code is provided. + + Valid Examples for `+13334445555`: + - `%2B13334445555` + - `13334445555` + - `3334445555` + + + +### Body + + + Provide instructions, relevant information, and examples of the ideal conversation flow. + + For inbound numbers, consider including additional context about the purpose of the call, and what types of callers to expect. + + + #### Out-of-the-Box Behaviors (Summarized): + - Speech pattern: Direct, concise, casual + - Spells out symbols, acronyms, abbreviations, percentages, etc. ($4,000,000 -> "four million dollars") + - Asks clarifying questions + + #### Prompting Tips: + - Want to easily test out exactly how your agent will behave? + - [Try out Agent Testing!](https://app.bland.ai/home?page=testing) + - Aim for less than >2,000 characters where possible. + - Simple, direct prompts are the most predictable and reliable. + - Frame instructions positively: + - `"Do this"` rather than `"Don't do this"`. + - Ex. "Keep the conversation casual" rather than "Don't be too formal". + - This gives concrete examples of what to do, instead of leaving expected behavior open to interpretation. + + + + + + Set your agent's voice - all available voices can be found with the [List Voices](/api-v1/get/voices) endpoint. + + + + Define a JSON schema for how you want to get information about the call - information like email addresses, names, appointment times or any other type of custom data. + +In the webhook response or whenever you retrieve call data later, you'll get the data you defined back under `analysis`. + +For example, if you wanted to retrieve this information from the call: + +```json +"analysis_schema": { + "email_address": "email", + "first_name": "string", + "last_name": "string", + "wants_to_book_appointment": "boolean", + "appointment_time": "YYYY-MM-DD HH:MM:SS" +} +``` + +You would get it filled out like this in your webhook once the call completes: + +```json +"analysis": { + "email_address": "johndoe@gmail.com", + "first_name": "John", + "last_name": "Doe", + "wants_to_book_appointment": true, + "appointment_time": "2024-01-01 12:00:00" +} +``` + + + + + Add any additional information you want to associate with the call. This can be useful for tracking or categorizing + calls. + + + + Set the pathway that your agent will follow. This will override the `prompt` field, so there is no need to pass the 'prompt' field if you are setting a pathway. + + Warning: Setting a pathway will set the following fields to `null` / their default value - `prompt`, `first_sentence`, `model`, `dynamic_data`, `tools` + + Set to `null` or an empty string to clear the pathway. + + + + + Select a supported language of your choice. Optimizes every part of our API for that language - transcription, speech, + and other inner workings. Supported Languages and their codes: - English: ```ENG``` - Spanish: ```ESP``` - French: + ```FRE``` - Polish: ```POL``` - German: ```GER``` - Italian: ```ITA``` - Brazilian Portuguese: ```PBR``` - Portuguese: + ```POR``` + + + + The webhook should be a http / https callback url. We will send the call_id + and transcript to this URL after the call completes. This can be useful if you + want to have real time notifications when calls finish. + +Set to `null` or an empty string to clear the webhook. + + + + + Give your agent the ability to transfer calls to a set of phone numbers. + +Overrides `transfer_phone_number` if a `transfer_list.default` is specified. + +Will default to `transfer_list.default`, or the chosen phone number. + +Example usage to route calls to different departments: + +```json +"transfer_list": { + "default": "+12223334444", + "sales": "+12223334444", + "support": "+12223334444", + "billing": "+12223334444" +} +``` + + + + + Select a model to use for your call. + +Options: `base`, `turbo` and `enhanced`. + +In nearly all cases, `enhanced` is the best choice for now. + + + +There are three different ways to use Bland: + +- `model: base` + + - The original, follows scripts/procedures most effectively. + - Supports all features and capabilities. + - Best for Custom Tools + +- `model: enhanced` + + - Much faster latency and very conversational, works best with objective-based prompts. + - Supports all features and capabilities. + +- `model: turbo` + + - The absolute fastest latency possible, can be verbose at times + - Limited capabilities currently (excludes Transferring, IVR navigation, Custom Tools) + - Extremely realistic conversation capabilities + + + + + + + A phone number that the agent can transfer to under specific conditions - such as being asked to speak to a human or supervisor. + +Set to `null` to remove. + + + For best results: + - Specify conditions that the agent should transfer to a human under (examples are great!) + - In the `task`, refer to the action solely as "transfer" or "transferring". + - Alternate phrasing such as "swap" or "switch" can mislead the agent, causing the action to be ignored. + + + + + + To record your phone call, set `record` to true. When your call completes, you can access through the `recording_url` + field in the call details or your webhook. + + + + A phrase that your call will start with instead of a generating one on the fly. This works both with and without `wait_for_greeting`. Can be more than one sentence, but must be less than 200 characters. + +To remove, set to `null` or an empty string. + + + + + Interact with the real world through API calls. + +Detailed tutorial here: [Custom Tools](/tutorials/custom-tools) + + + + + Integrate data from external APIs into your agent's knowledge. + +Set to `null` or an empty string to clear dynamic data settings. + +Detailed usage in the [Send Call](/api-v1/post/calls) endpoint. + + + + + When you increase the interruption latency, you force the AI phone agent to listen longer before responding. In practice, increasing the threshold results in less interruptions and more latency. + +Try setting the threshold to `500` milliseconds. You'll encounter higher latency, but you'll be interrupted less frequently. + +Set to `null` to reset to default. + + + + + The maximum duration that calls to your agent can last before being automatically terminated. + +Set to `null` to reset to default. + + + +### Response + + + Whether the update was successful or not - will be `success` or `error`. + + + + A message describing the status of the update. + + + + An object containing the updated settings for the inbound number. + + + + If the update was unsuccessful, this will contain the settings that failed to update. Useful to determine how your + request is being interpreted on our end. + + + + +```json Response +{ + "status": "success", + "message": "Successfully updated number +18584139939.", + "updates": { + "prompt": "(Your prompt)", + "voice": "maya", + "webhook": null, + "first_sentence": "Roberta speaking, how can I help you?", + "record": false, + "max_duration": 30, + "model": "enhanced" + //... + } +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-purchase.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-purchase.mdx new file mode 100644 index 00000000000..af050d6c071 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/inbound-purchase.mdx @@ -0,0 +1,64 @@ +--- +title: Purchase Inbound number +subtitle: >- + Purchase and configure a new inbound phone number. ($15/mo. subscription using + your stored payment method). +slug: api-v1/post/inbound-purchase +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + Choose a three-digit area code for your phone number. If set as a parameter, a number will only be purchased by exact + match if available. + + + + This defines how the AI will start the conversation, information available to it, and its behaviors. Matches how the + outbound `task` parameter functions. + + + + Choose a country code for your phone number. + +Options: `"US"` or `"CA"` for Canada. For others, please contact support. + + + + + The webhook should be a http / https callback url. We will send the call_id and transcript to this URL after the call + completes. This can be useful if you want to have real time notifications when calls finish. + + + + Specify an exact phone number you'd like to use. If provided, will override the `area_code` parameter and does not fall back to any other number. + +Example of the correct format (Note the `"+1"` is mandatory): `"+12223334444"` + + + +### Response + + + The created phone number, will be in the following format: `+1XXXXXXXXXX` + +Example: `+18582814611` + + + + + +```json Response +{ + "phone_number": "+18582814611" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/outbound-purchase.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/outbound-purchase.mdx new file mode 100644 index 00000000000..2df4623fe2c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/outbound-purchase.mdx @@ -0,0 +1,40 @@ +--- +title: Purchase Outbound number +subtitle: >- + Purchase and configure a new inbound phone number. ($15/mo. subscription using + your stored payment method). +slug: api-v1/post/outbound-purchase +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + Choose a three-digit area code for your phone number. If set as a parameter, a number will only be purchased by exact + match if available. + + +### Response + + + The created phone number, will be in the following format: `+1XXXXXXXXXX` + +Example: `+18582814611` + + + + + +```json Response +{ + "phone_number": "+18582814611" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-analyze.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-analyze.mdx new file mode 100644 index 00000000000..4cb5d651e71 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-analyze.mdx @@ -0,0 +1,53 @@ +--- +title: SMS Conversation Analysis +subtitle: Answer questions and extract information from an SMS conversation. +slug: api-v1/post/sms-analyze +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + An overarching goal for the information you want to extract from the SMS messages. + + + +An array of questions that you want the AI to answer, along with their return types. + +For example: + +```json +{ + "answers": [ + ["When does Bob want to move?", "time"], + ["Summarize the call.", "summary"] + ] +} +``` + + + + + The phone number that received the messages. + + + + The human/other phone number in the conversation. + + + + +```json Response +{ + "status": "success", + "message": "Successfully analyzed SMS messages.", + "answers": ["Bob prefers to have movers come in the morning.", "..."] +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-check-registration.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-check-registration.mdx new file mode 100644 index 00000000000..d4f6dd44150 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-check-registration.mdx @@ -0,0 +1,30 @@ +--- +title: Check SMS A2P status +subtitle: Check the status of an A2P registration. +slug: api-v1/post/sms-check-registration +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + The `registration_id` for the a2p registration. + + + + +```json Response +{ + status: "pending" || "approved" || "failed", + brandType: //string of the brand type, + failureReason: null || "reason here" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-get-messages.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-get-messages.mdx new file mode 100644 index 00000000000..8dee1eb64ec --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-get-messages.mdx @@ -0,0 +1,34 @@ +--- +title: Get SMS Messages +subtitle: Get the list of SMS messages for a given conversation. +slug: api-v1/post/sms-get-messages +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + +The `to` number in the conversation. This is the number you *do not* own. + + +The `from` number in the conversation. This is the number you *do* own. + +**Please note any ordering of numbers will work** + + + + + +```json Response +{ + "messages": "[]" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-prompt-update.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-prompt-update.mdx new file mode 100644 index 00000000000..7713c499965 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-prompt-update.mdx @@ -0,0 +1,35 @@ +--- +title: Update SMS Prompt +subtitle: Update your SMS agent's prompt. +slug: api-v1/post/sms-prompt-update +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to update. + + + + The prompt for the AI to use when replying. + + + + Pass in an array of strings, that if present, the AI should not respond to. Set to `null` to disable. + + + + +```json Response +{ + "status": "Prompt updated" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-submit-reg.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-submit-reg.mdx new file mode 100644 index 00000000000..3584d372992 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-submit-reg.mdx @@ -0,0 +1,72 @@ +--- +title: A2P Registration +subtitle: >- + Learn how to register your A2P brand using our API. This guide covers all you + need to know about making a POST request, including required parameters, error + handling, and response expectations. +slug: api-v1/post/sms-submit-reg +--- + + +# Registering an A2P Brand via API + +This documentation provides detailed information on how to register an Application-to-Person (A2P) brand by making a POST request to our API. The process involves submitting your brand's details for registration and verification purposes. + +A2P Registration is required for _all_ businesses who wish to send SMS. There can be signifcant fines for any non compliant messages. A2P Registration can take 2 days -> 2 Weeks. + +## Endpoint + +`POST /api/registerA2PBrand` + +## Required Headers + +- `Authorization`: Your API key for authentication. + +## Request Parameters + +Your request should include a JSON body with the following parameters: + +- `businessName` (string): The legal name of your business. +- `ein` (string): Your Employer Identification Number. +- `vertical` (string): Industry vertical. Possible values include: "AUTOMOTIVE", "AGRICULTURE", "BANKING", "CONSTRUCTION", "CONSUMER", "EDUCATION", "ENGINEERING", "ENERGY", "OIL_AND_GAS", "FAST_MOVING_CONSUMER_GOODS", "FINANCIAL", "FINTECH", "FOOD_AND_BEVERAGE", "GOVERNMENT", "HEALTHCARE", "HOSPITALITY", "INSURANCE", "LEGAL", "MANUFACTURING", "MEDIA", "ONLINE", "PROFESSIONAL_SERVICES", "RAW_MATERIALS", "REAL_ESTATE", "RELIGION", "RETAIL", "JEWELRY", "TECHNOLOGY", "TELECOMMUNICATIONS", "TRANSPORTATION", "TRAVEL", "ELECTRONICS", "NOT_FOR_PROFIT" +- `address` (string): The business address. +- `city` (string): The city of your business. +- `state` (string): The state of your business. Must be a valid US state code. +- `postalCode` (string): The postal code of your business. +- `country` (string): The country of your business. +- `email` (string): The email address for your business. +- `type` (string): Legal structure of the business. Possible values: "Partnership", "Limited Liability Corporation", "Co-operative", "Non-profit Corporation", "Corporation" +- `website` (string): Your business's website URL. +- `opt_in_info` (string): Information regarding opt-in procedures for your messaging service. EX: "Customers must explicitly consent on our website and during the phone call." +- `messageSamples` (array): An array of three strings, each a sample message you plan to use. +- `trusted_user` (object): An object containing details about the trusted user registering the brand. Includes `position`, `last_name`, `phone_number`, `first_name`, and `email`. + +Ex. of trusted_user obj: + +```json +{ + "position": "CEO" //must be C Suite or VP, + "last_name":"Smith", + "first_name":"John" +} +``` + +## Error Handling + +Our API provides detailed error messages to help you understand what went wrong in case of a failure: + +- `400 Bad Request`: This response occurs if any required fields are missing in your request or if the state code is invalid. The response body will include a message specifying the missing or incorrect fields. +- `500 Internal Server Error`: Indicates an unexpected error on the server side. The response body will contain an error message with more details. + +## Successful Response + +A successful request returns a `200 OK` status code with a JSON body containing a message indicating the registration was successful and any relevant data or identifiers related to the A2P brand registration. + +**\*Important\*\*** The Brand Registration can take several attempts and days to weeks to complete. This success only indicates we _submitted_ the registration correctly. + +```json +{ + "message": "A2P Brand registration successful.", + "data": {...} +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-toggle-human.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-toggle-human.mdx new file mode 100644 index 00000000000..a3c86027dd5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-toggle-human.mdx @@ -0,0 +1,36 @@ +--- +title: Toggle SMS Reply Method +subtitle: Turn on or off the AI replying for a given phone number. +slug: api-v1/post/sms-toggle-human +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + The phone number to update. + + + +Turn human mode on or off. + +`true` means that the AI will _not_ reply. +`false` means the AI will reply + + + + + +```json Response +{ + "status": "Turned human mode on" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-webhook-update.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-webhook-update.mdx new file mode 100644 index 00000000000..3445c3104ff --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/sms-webhook-update.mdx @@ -0,0 +1,31 @@ +--- +title: Update SMS Webhook +subtitle: Update the webhook for a given phone number. +slug: api-v1/post/sms-webhook-update +--- + + +### Headers + + + Your API key for authentication. + +### Body + + + The phone number to update. + + + + The webhook to fire when an SMS is received. + + + + +```json Response +{ + "status": "success" +} +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-disable.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-disable.mdx new file mode 100644 index 00000000000..67c194236ca --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-disable.mdx @@ -0,0 +1,49 @@ +--- +title: Disable Subaccount +subtitle: >- + Immediately disables a subaccount and transfers any remaining credits back to + the parent account. +slug: api-v1/post/subaccounts-id-disable +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the subaccount to which you want to transfer credits. + + +### Response + + + Whether the subaccount was successfully disabled. + + + + The affected subaccount's unique identifier. + + + + The amount of credits transferred back to the parent account. + + + + The new balance of the parent account after the transfer. + + + +```json +{ + "status": "success", + "subaccount_id": "1234567890", + "transferred_amount": 1000, + "new_parent_balance": 5000 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-rotate.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-rotate.mdx new file mode 100644 index 00000000000..3fcacb17d9f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-rotate.mdx @@ -0,0 +1,42 @@ +--- +title: Rotate Subaccount API Key +subtitle: Replace the API key of a subaccount with a new one. +slug: api-v1/post/subaccounts-id-rotate +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the subaccount to which you want to transfer credits. + + +### Response + + + Whether the subaccount creation succeeded. + + + + The affected subaccount's unique identifier. + + + + The new API key for the subaccount. + + + +```json +{ + "status": "success", + "subaccount_id": "1234567890", + "api_key": "sub-sk-1234567890" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-transfer.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-transfer.mdx new file mode 100644 index 00000000000..de7574d33c1 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts-id-transfer.mdx @@ -0,0 +1,51 @@ +--- +title: Transfer Credit to a Subaccount +subtitle: Transfer API credits from your account to a subaccount. +slug: api-v1/post/subaccounts-id-transfer +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The unique identifier of the subaccount to which you want to transfer credits. + + +### Body + + + This many of your API credits will be transferred to the subaccount. + + The balance must be a positive integer that is at least 1, and less than your current account balance. + + + +### Response + + + Whether the subaccount creation succeeded. + + + + The new balance of your account after the transfer. + + + + The new balance of the subaccount after the transfer. + + + +```json +{ + "status": "success", + "new_parent_balance": 1200, + "new_subaccount_balance": 500 +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts.mdx new file mode 100644 index 00000000000..fff14f6200e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/subaccounts.mdx @@ -0,0 +1,71 @@ +--- +title: Create Subaccount +subtitle: Provision a new subaccount with separate billing, access and usage. +slug: api-v1/post/subaccounts +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + Moves a portion of your API credit balance to the newly created subaccount. + + The balance must be a positive integer that is at least 10, and less than your current account balance. + + Special per-minute pricing plans carry over automatically to the subaccount, while increased rate limits do not. + + Unused credits can be reclaimed later with the subaccount's balance if needed. + + + + + The first name of the user who will be using the subaccount. + + + + The last name of the user who will be using the subaccount. + + + + Whether you will be able to log in to the subaccount through the Dev Portal at `app.bland.ai`. + + This enables you to set up credit card information, view and monitor usage, and further manage the subaccount as needed. + + The subaccount user will not be able to log into the Dev Portal unless you explicitly enable it by adding them as an Authorized User. + + + +### Response + + + Whether the subaccount creation succeeded. + + + + The unique identifier for the newly created subaccount. + + + + The API key that the new subaccount can use to authenticate requests. + + This is the only information that the subaccount user will need to start using the API. + + Do not use this as an identifier for the subaccount, since it can be rotated. + + + + +```json +{ + "status": "success", + "subaccount_id": "1234567890", + "subaccount_key": "sub-sk-1234567890" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/tools-tool-id.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/tools-tool-id.mdx new file mode 100644 index 00000000000..e068e4af003 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/tools-tool-id.mdx @@ -0,0 +1,354 @@ +--- +title: Update Custom Tool +subtitle: Change your Custom Tool's parameters and characteristics. +slug: api-v1/post/tools-tool-id +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The ID of the Custom Tool you want to update. + + +### Body + + + This is the name that the AI using the tool will see. + + Some other internal tools are named `Speak`, `Wait`, `Transfer` and `Finish` - Custom Tools cannot share these names. + + We've made a list of reserved words that can confuse the AI that cannot be included: + - `input` + - `speak` + - `transfer` + - `switch` + - `wait` + - `finish` + - `press` + - `button` + - `say` + - `pause` + - `record` + - `play` + - `dial` + - `hang` + + Choosing too similar of names to the default tools could cause the AI to select the wrong one, so decriptive two to three-word names are preferred. + + + + + This is the description that the AI using the tool will see. + + Describe the effect of what the tool does or any special instructions. + + For reference, here are the default tools' descriptions: + - `Speak`: Talk to the person on the other end of the line + - `Press Buttons`: Presses buttons on phone. Each character is a different button. + - `Wait`: Wait and go silent for an extended period of time (only use if absolutely necessary). + - `Finish`: Say a goodbye message and end the call once completed. + + + + + This is the text that the AI will say while it uses the tool. + + For example, if the tool is a "GenerateQuote" tool, the speech might be "Please wait while I get you your quote." + + Since tools can be verbally interrupted, shorter messages that tell the user what the tool/AI are doing are best. + + Special Note: You can have the AI dynamically generate speech by defining `input.speech` in the `input_schema`. + + + +```json +{ + "input_schema": { + "example": { + "speech": "Checking your account details right now John!", + "name": "John Doe", + "email": "johndoe@gmail.com" + }, + "type": "object", + "properties": { + "speech": { + "type": "string" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["speech", "name", "email"] + } +} +``` + + + + + + + This is the endpoint of the external API that the tool will call. + + It must begin with `https://` and be a valid URL. + + + + + This is the HTTP method that the tool will use to call the external API. + + Valid options are `GET` and `POST`. + + + + + `SUPPORTS PROMPT VARIABLES` + + These are the headers that the tool will send to the external API. + + The headers must be in JSON format. + + Since prompt variables are supported, you can use them in the headers to send dynamic information to the external API. + + + +```json +// At the top level of your send call request you can define variables that you can access later using the double curly braces/dot syntax. +{ + "request_data": { + "api_key": "sk-1234567890" + }, + "tools": [ + { + "headers": { + "Authorization": "Bearer {{api_key}}" + } + } + ] +} +``` + + + + + + + `SUPPORTS PROMPT VARIABLES` + + This is the body that the tool will send to the external API. + + The body must be in JSON format. + + This is the most common place to use Prompt Variables with AI input. + + Note: `GET` requests do not have a body. + + + +```json + // AI-generated input is created as the `input` Prompt Variable - and the structure is defined by the input schema. + // `input` will match the structure of `input_schema.example` if it is defined. + { + "input_schema": { + "example": { + "name": "John Doe", + "email": "johndoe@gmail.com" + } + } + "body": { + "name": "{{input.name}}", + "email": "{{input.email}}" + } + } +``` + + + + + + + `SUPPORTS PROMPT VARIABLES` + + Append query parameters to the URL. + + The query must be in JSON format. + + This is generally used with GET requests and built-in Prompt Variables like `"{{phone_number}}"` or `"{{call_id}}"`. + + + +```json +// appends ?pn={{phone_number}}&callId={{call_id}} to the URL +{ + "query": { + "pn": "{{phone_number}}", + "callId": "{{call_id}}" + } +} +``` + + + + + + + This is the schema that the AI input must match for the tool to be used. + + The schema must be in JSON format. + + The schema is used to validate the AI input before the tool is used. + + If the AI input does not match the schema, the tool will not be used and the AI will move on to the next tool. + + `input_schema.example` can be used to enhance the AI's understanding of the input structure and helps significantly with structured or nested data. + + Special Note: `input_schema` does not require strict JSON schema structure, and creativity is encouraged. + + [Look here for a general guide on JSON schema structures.](https://json-schema.org/learn/getting-started-step-by-step) + + Non-traditional JSON schema structures are supported as well, like these examples: + - "options": "monday, wednesday, friday" + - "date": "YYYY-MM-DD" + - "time": "HH:MM:SS (AM|PM)" + - "phone_number": "+1XXX-XXX-XXXX" + + Agent input can be nested, and the will be transformed into JSON even if it's initially a string. + + + +```json +{ + "input_schema": { + "example": { + "name": "John Doe", + "email": "johndoe@gmail.com" + }, + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["name", "email"] + }, + // both of these methods are identical, since {{input}} will be transformed into JSON + "body": "{{input}}", + "body": { + "name": "{{input.name}}", + "email": "{{input.email}}" + } +} +``` + + + + + + + Define how you would like to extract data from the response. + + By default, the entire response body is stored in the `{{data}}` Prompt Variable. + + The path to the data you want must be in JSON Path format. Generally this means using dot notation to traverse the JSON object and is only required if you need to use that information on other tools or the response is too large. + + Example: + +```json + // If the external API response is: + { + "available_times": [ + { + "time": "10:00 AM", + "date": "2022-01-01" + }, + { + "time": "11:00 AM", + "date": "2022-01-01" + } + ], + "store_hours": { + "open": "9:00 AM", + "close": "5:00 PM" + }, + "address_info": { + "street": "123 Main St", + "city": "Anytown", + "state": "CA", + "zip": "12345" + } + } + + // You can extract new Prompt Variables like this: + { + "response": { + "available_times": "$.available_times", + "store_hours": "$.store_hours", + "address_info": "$.address_info", + "zip_code": "$.address_info.zip" + } + } + + // And then it'll automatically replace them elsewhere (like in the `task`/`prompt`) + { + "task": "The store is open from {{store_hours.open}} to {{store_hours.close}}.", + "prompt": "The store is located at {{address_info.street}}, {{address_info.city}}, {{address_info.state}} {{zip_code}}." + } +``` + + + + + This is the maximum time in milliseconds that the tool will wait for a response from the external API. + + If the external API does not respond within this time, the tool will fail and the AI will move on to the next tool. + + The default timeout is 10 seconds (10000 milliseconds). + + To always wait for a response, set the timeout to an extremely high value like 99999999. + + + +### Response + + + Whether the tool creation succeeded. + + + + A tool id that you can use to reference the tool in the future. + + In a Send Call request, you could pass this tool id in instead of the full Custom Tool object like so: + + ```json + { + "tools": [ + "TL-1234567890" // tool_id (instead of the full Custom Tool object) + ] + } + ``` + + + + +```json +{ + "status": "success", + "tool_id": "TL-1234567890" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/tools.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/tools.mdx new file mode 100644 index 00000000000..711a608537c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/tools.mdx @@ -0,0 +1,348 @@ +--- +title: Create a Custom Tool +subtitle: Create a Custom Tool that can take AI input and call external APIs. +slug: api-v1/post/tools +--- + + +### Headers + + + Your API key for authentication. + + +### Body + + + This is the name that the AI using the tool will see. + + Some other internal tools are named `Speak`, `Wait`, `Transfer` and `Finish` - Custom Tools cannot share these names. + + We've made a list of reserved words that can confuse the AI that cannot be included: + - `input` + - `speak` + - `transfer` + - `switch` + - `wait` + - `finish` + - `press` + - `button` + - `say` + - `pause` + - `record` + - `play` + - `dial` + - `hang` + + Choosing too similar of names to the default tools could cause the AI to select the wrong one, so decriptive two to three-word names are preferred. + + + + + This is the description that the AI using the tool will see. + + Describe the effect of what the tool does or any special instructions. + + For reference, here are the default tools' descriptions: + - `Speak`: Talk to the person on the other end of the line + - `Press Buttons`: Presses buttons on phone. Each character is a different button. + - `Wait`: Wait and go silent for an extended period of time (only use if absolutely necessary). + - `Finish`: Say a goodbye message and end the call once completed. + + + + + This is the text that the AI will say while it uses the tool. + + For example, if the tool is a "GenerateQuote" tool, the speech might be "Please wait while I get you your quote." + + Since tools can be verbally interrupted, shorter messages that tell the user what the tool/AI are doing are best. + + Special Note: You can have the AI dynamically generate speech by defining `input.speech` in the `input_schema`. + + + +```json +{ + "input_schema": { + "example": { + "speech": "Checking your account details right now John!", + "name": "John Doe", + "email": "johndoe@gmail.com" + }, + "type": "object", + "properties": { + "speech": { + "type": "string" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["speech", "name", "email"] + } +} +``` + + + + + + + This is the endpoint of the external API that the tool will call. + + It must begin with `https://` and be a valid URL. + + + + + This is the HTTP method that the tool will use to call the external API. + + Valid options are `GET` and `POST`. + + + + + `SUPPORTS PROMPT VARIABLES` + + These are the headers that the tool will send to the external API. + + The headers must be in JSON format. + + Since prompt variables are supported, you can use them in the headers to send dynamic information to the external API. + + + +```json +// At the top level of your send call request you can define variables that you can access later using the double curly braces/dot syntax. +{ + "request_data": { + "api_key": "sk-1234567890" + }, + "tools": [ + { + "headers": { + "Authorization": "Bearer {{api_key}}" + } + } + ] +} +``` + + + + + + + `SUPPORTS PROMPT VARIABLES` + + This is the body that the tool will send to the external API. + + The body must be in JSON format. + + This is the most common place to use Prompt Variables with AI input. + + Note: `GET` requests do not have a body. + + + +```json + // AI-generated input is created as the `input` Prompt Variable - and the structure is defined by the input schema. + // `input` will match the structure of `input_schema.example` if it is defined. + { + "input_schema": { + "example": { + "name": "John Doe", + "email": "johndoe@gmail.com" + } + } + "body": { + "name": "{{input.name}}", + "email": "{{input.email}}" + } + } +``` + + + + + + + `SUPPORTS PROMPT VARIABLES` + + Append query parameters to the URL. + + The query must be in JSON format. + + This is generally used with GET requests and built-in Prompt Variables like `"{{phone_number}}"` or `"{{call_id}}"`. + + + +```json +// appends ?pn={{phone_number}}&callId={{call_id}} to the URL +{ + "query": { + "pn": "{{phone_number}}", + "callId": "{{call_id}}" + } +} +``` + + + + + + + This is the schema that the AI input must match for the tool to be used. + + The schema must be in JSON format. + + The schema is used to validate the AI input before the tool is used. + + If the AI input does not match the schema, the tool will not be used and the AI will move on to the next tool. + + `input_schema.example` can be used to enhance the AI's understanding of the input structure and helps significantly with structured or nested data. + + Special Note: `input_schema` does not require strict JSON schema structure, and creativity is encouraged. + + [Look here for a general guide on JSON schema structures.](https://json-schema.org/learn/getting-started-step-by-step) + + Non-traditional JSON schema structures are supported as well, like these examples: + - "options": "monday, wednesday, friday" + - "date": "YYYY-MM-DD" + - "time": "HH:MM:SS (AM|PM)" + - "phone_number": "+1XXX-XXX-XXXX" + + Agent input can be nested, and the will be transformed into JSON even if it's initially a string. + + + +```json +{ + "input_schema": { + "example": { + "name": "John Doe", + "email": "johndoe@gmail.com" + }, + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["name", "email"] + }, + // both of these methods are identical, since {{input}} will be transformed into JSON + "body": "{{input}}", + "body": { + "name": "{{input.name}}", + "email": "{{input.email}}" + } +} +``` + + + + + + + Define how you would like to extract data from the response. + + By default, the entire response body is stored in the `{{data}}` Prompt Variable. + + The path to the data you want must be in JSON Path format. Generally this means using dot notation to traverse the JSON object and is only required if you need to use that information on other tools or the response is too large. + + Example: + +```json + // If the external API response is: + { + "available_times": [ + { + "time": "10:00 AM", + "date": "2022-01-01" + }, + { + "time": "11:00 AM", + "date": "2022-01-01" + } + ], + "store_hours": { + "open": "9:00 AM", + "close": "5:00 PM" + }, + "address_info": { + "street": "123 Main St", + "city": "Anytown", + "state": "CA", + "zip": "12345" + } + } + + // You can extract new Prompt Variables like this: + { + "response": { + "available_times": "$.available_times", + "store_hours": "$.store_hours", + "address_info": "$.address_info", + "zip_code": "$.address_info.zip" + } + } + + // And then it'll automatically replace them elsewhere (like in the `task`/`prompt`) + { + "task": "The store is open from {{store_hours.open}} to {{store_hours.close}}.", + "prompt": "The store is located at {{address_info.street}}, {{address_info.city}}, {{address_info.state}} {{zip_code}}." + } +``` + + + + + This is the maximum time in milliseconds that the tool will wait for a response from the external API. + + If the external API does not respond within this time, the tool will fail and the AI will move on to the next tool. + + The default timeout is 10 seconds (10000 milliseconds). + + To always wait for a response, set the timeout to an extremely high value like 99999999. + + + +### Response + + + Whether the tool creation succeeded. + + + + A tool id that you can use to reference the tool in the future. + + In a Send Call request, you could pass this tool id in instead of the full Custom Tool object like so: + + ```json + { + "tools": [ + "TL-1234567890" // tool_id (instead of the full Custom Tool object) + ] + } + ``` + + + + +```json +{ + "status": "success", + "tool_id": "TL-1234567890" +} +``` + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/voices-id-sample.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/voices-id-sample.mdx new file mode 100644 index 00000000000..10f6c37c0ee --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/api-v1/post/voices-id-sample.mdx @@ -0,0 +1,46 @@ +--- +title: Generate Audio Sample +subtitle: Generate an audio sample for a voice. +slug: api-v1/post/voices-id-sample +--- + + +### Headers + + + Your API key for authentication. + + +### Path Parameters + + + The ID of the voice to generate the audio sample for, or it's name (like "maya"). + + +### Request Body + + + The text content to be spoken in the voice sample. + +Character limit: `200` characters. + + + + + Alternate `voice_settings` can be passed in to override the preset's default settings. + + + + The language of the text content. Default is `ENG`. + +Some other language codes: "ESP", "GER", "FRE" + + + +### Response + + + The generated audio file of the spoken text using the specified or overridden voice preset settings. + + +```json (Generated audio file) ``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/developerportal.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/developerportal.png new file mode 100644 index 00000000000..c906cdfd631 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/developerportal.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/docs.yml b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/docs.yml new file mode 100644 index 00000000000..63ce8f5b8f3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/docs.yml @@ -0,0 +1,229 @@ +instances: [] +title: Bland AI +favicon: logo.png +logo: + light: logo/light.svg + dark: logo/dark.svg + height: 28 +colors: + accentPrimary: + dark: '#0b0a18' + light: '#847AF9' + background: + dark: '#0A141B' +tabs: + api-v1: + slug: api-v1 + displayName: V1 API Reference + documentation: + displayName: Documentation + slug: documentation +navigation: + - tab: api-v1 + layout: + - section: Basic + contents: + - page: Send Call (Simple) + path: api-v1/post/calls-simple.mdx + - page: Send Call using Pathways (Simple) + path: api-v1/post/calls-simple-pathway.mdx + - section: Calls + contents: + - page: Send Call + path: api-v1/post/calls.mdx + - page: Analyze Call with AI + path: api-v1/post/calls-id-analyze.mdx + - page: Stop Active Call + path: api-v1/post/calls-id-stop.mdx + - page: List Calls + path: api-v1/get/calls.mdx + - page: Call Details + path: api-v1/get/calls-id.mdx + - page: Audio Recording + path: api-v1/get/calls-id-recording.mdx + - page: Get corrected transcripts + path: api-v1/get/calls-corrected-transcript.mdx + - section: Web Agents + contents: + - page: Create a Web Agent + path: api-v1/post/agents.mdx + - page: Update Web Agent Settings + path: api-v1/post/agents-id.mdx + - page: Authorize a Web Agent Call + path: api-v1/post/agents-id-authorize.mdx + - page: Delete Web Agent + path: api-v1/post/agents-id-delete.mdx + - page: List Web Agents + path: api-v1/get/agents.mdx + - section: Inbound Numbers + contents: + - page: Purchase Inbound number + path: api-v1/post/inbound-purchase.mdx + - page: Update Inbound Details + path: api-v1/post/inbound-number-update.mdx + - page: List Inbound Numbers + path: api-v1/get/inbound.mdx + - page: Inbound Number Details + path: api-v1/get/inbound-number.mdx + - section: Outbound Numbers + contents: + - page: Purchase Outbound number + path: api-v1/post/outbound-purchase.mdx + - page: List Outbound Numbers + path: api-v1/get/outbound.mdx + - section: Voices + contents: + - page: List Voices + path: api-v1/get/voices.mdx + - page: Voice Details + path: api-v1/get/voices-id.mdx + - page: Generate Audio Sample + path: api-v1/post/voices-id-sample.mdx + - section: Custom Tools + contents: + - page: Create a Custom Tool + path: api-v1/post/tools.mdx + - page: Update Custom Tool + path: api-v1/post/tools-tool-id.mdx + - page: List Custom Tools + path: api-v1/get/tools.mdx + - page: Custom Tool Details + path: api-v1/get/tools-tool-id.mdx + - section: Custom Twilio Accounts + contents: + - page: Create Encrypted Key + path: api-v1/post/accounts.mdx + - page: Delete Encrypted Key + path: api-v1/post/accounts-delete.mdx + - page: Upload Inbound Phone Numbers + path: api-v1/post/inbound-insert.mdx + - page: Delete Inbound Phone Number + path: api-v1/post/inbound-number-delete.mdx + - section: Subaccounts + contents: + - page: Create Subaccount + path: api-v1/post/subaccounts.mdx + - page: Transfer Credit to a Subaccount + path: api-v1/post/subaccounts-id-transfer.mdx + - page: Rotate Subaccount API Key + path: api-v1/post/subaccounts-id-rotate.mdx + - page: Disable Subaccount + path: api-v1/post/subaccounts-id-disable.mdx + - page: List Subaccounts + path: api-v1/get/subaccounts.mdx + - page: List Subaccount Details + path: api-v1/get/subaccounts-id.mdx + - section: Batches + contents: + - page: Send a Batch of Calls + path: api-v1/post/batches.mdx + - page: Analyze Batch with AI + path: api-v1/post/batches-id-analyze.mdx + - page: Stop Active Batch + path: api-v1/post/batches-id-stop.mdx + - page: List Batches + path: api-v1/get/batches.mdx + - page: Batch Details + path: api-v1/get/batches-id.mdx + - page: Retrieve Batch Analysis + path: api-v1/get/batches-id-analysis.mdx + - section: SMS + contents: + - page: A2P Registration + path: api-v1/post/sms-submit-reg.mdx + - page: Check SMS A2P status + path: api-v1/post/sms-check-registration.mdx + - page: Update SMS Prompt + path: api-v1/post/sms-prompt-update.mdx + - page: SMS Conversation Analysis + path: api-v1/post/sms-analyze.mdx + - page: Get SMS Messages + path: api-v1/post/sms-get-messages.mdx + - page: Toggle SMS Reply Method + path: api-v1/post/sms-toggle-human.mdx + - page: Update SMS Webhook + path: api-v1/post/sms-webhook-update.mdx + - section: Account + contents: + - page: Account Details + path: api-v1/get/me.mdx + - tab: documentation + layout: + - section: Get Started + contents: + - page: Welcome to Bland AI + path: welcome-to-bland.mdx + - page: Starter guide + path: starter-guide.mdx + - section: Bland Enterprise + contents: + - page: Custom Twilio Integration + path: enterprise-features/custom-twilio.mdx + - page: Fine-tuning & Custom LLMs + path: enterprise-features/custom-llm.mdx + - page: Unlimited support + path: enterprise-features/unlimited-support.mdx + - page: Custom languages & voices + path: enterprise-features/custom-tts.mdx + - page: Bring your own Twilio + path: enterprise-features/bring-your-own-twilio.mdx + - page: Enterprise rate limits + path: enterprise-features/enterprise-rate-limits.mdx + - section: Featured guides + contents: + - page: Bland & Botpress + path: featured-guides/bland-and-botpress.mdx + - section: Basic Tutorials + contents: + - page: Conversational Pathways + path: tutorials/pathways.mdx + - page: Custom Tools + path: tutorials/custom-tools.mdx + - page: Send your first phone call + path: tutorials/send-first-call.mdx + - page: Webhook Signing + path: tutorials/webhook-signing.mdx + - page: Send 1000 phone calls + path: tutorials/send-1000-calls-at-once.mdx + - page: Dynamic Data + path: tutorials/dynamic-data.mdx + - page: Setting max duration + path: tutorials/max-duration.mdx + - page: Live transfer + path: tutorials/live-transfer.mdx + - page: Webhooks + path: tutorials/webhooks.mdx + - section: Primary Endpoints + contents: + - page: Make a call + path: api-reference/endpoint/call.mdx + - page: Get transcript + path: api-reference/endpoint/logs.mdx + - page: Wait on Hold (HoldForMe) + path: api-reference/endpoint/hold.mdx + - page: End phone call + path: api-reference/endpoint/end.mdx + - page: Validate Dynamic Data + path: api-reference/endpoint/dynamic_validate.mdx + - page: Purchase inbound number + path: api-reference/endpoint/purchase.mdx + - page: Update Inbound Prompt + path: api-reference/endpoint/inbound_prompt.mdx + - page: Get recording + path: api-reference/endpoint/recording.mdx + - section: Batch Endpoints + contents: + - page: Batch Calling + path: api-reference/batch-endpoint/batch.mdx + - page: Retrieve a Batch + path: api-reference/batch-endpoint/batch_get.mdx + - page: Retrieve All Batches + path: api-reference/batch-endpoint/batches_get.mdx + - page: Cancel Batch + path: api-reference/batch-endpoint/batch_stop.mdx + - page: Cancel All Batch Calls + path: api-reference/batch-endpoint/end_batches.mdx + - section: Hold Endpoint + contents: + - page: Wait on Hold (HoldForMe) + path: api-reference/hold-endpoint/hold.mdx diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/bring-your-own-twilio.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/bring-your-own-twilio.mdx new file mode 100644 index 00000000000..0d900e78eff --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/bring-your-own-twilio.mdx @@ -0,0 +1,15 @@ +--- +title: Bring your own Twilio +slug: enterprise-features/bring-your-own-twilio +--- + + +Enterprise customers can create and connect their own Twilio account to Bland. + +Features include: + +1. Full ownership of telephony infrastructure +2. Ability to connect to existing telephony infrastructure +3. Closer control of spam risk and any discounted rates already included on the account + +Reach out for enterprise access, here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-llm.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-llm.mdx new file mode 100644 index 00000000000..86102e28cce --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-llm.mdx @@ -0,0 +1,37 @@ +--- +title: Fine-tuning & Custom LLMs +slug: enterprise-features/custom-llm +--- + + +## Summary + +Bland will fine-tune a custom model for your enterprise using transcripts from succesful prior calls. Then Bland will host that LLM and provided dedicated infrastrucure to enable phone conversations with sub-second latency. + +Bland will also enable you to connect to a custom LLM & will host that LLM to drive latency down further. + +Get in touch here. + +## Background on fine-tuning + +Traditionally, most AI phone agents use private models from companies like OpenAI and Anthropic. Those LLMs are large, and perform best at following instructions and delivering high quality outputs. The downside, however, is they are very slow. Additionally, because they're general models, their personality, tone, and overall capabilities are limited. + +Conversely, open source models generally perform worse at a broad range of tasks. However, by fine-tuning an open-source model with examples of a given task, you can significantly improve it's performance at that task, even surpassing the capabilties of top-of-the-line models like GPT-4. + +## How do I fine-tune with Bland? + +To inquire about fine-tuning connect with the Bland team here. + +During the initial conversation you will discuss: + +1. The format of data we'll require +2. How long fine-tuning will take (typically one week) +3. Pricing (typically under five figures) + +## How do I bring my own LLM? + +The Bland team will advise on connection method, requirements for the connection, etc. + +Typically takes under 24 hours to set up after kickoff. + +Inquire here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-tts.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-tts.mdx new file mode 100644 index 00000000000..e5b1aff584f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-tts.mdx @@ -0,0 +1,11 @@ +--- +title: Custom languages & voices +slug: enterprise-features/custom-tts +--- + + +Enterprise customers can bring their own TTS service or work with Bland's engineering team to configure higher quality voice clones. + +Enterprise customers can also request foreign languages like German, Spanish, Italian, and Portoguese, and Bland's engineering team will set up different transcription and TTS to accomodate the request. + +Reach out for enterprise access, here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-twilio.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-twilio.mdx new file mode 100644 index 00000000000..c40b7825b0d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/custom-twilio.mdx @@ -0,0 +1,53 @@ +--- +title: Custom Twilio Integration +subtitle: Connect Bland to your own Twilio account +slug: enterprise-features/custom-twilio +--- + + +## Overview + +Enterprise customers can connect their own Twilio account to Bland. Easily bring over your existing phone numbers, integrations, and more. + +Pre-requisites: + +- Your own Twilio account +- An [Enterprise plan](https://forms.default.com/361589) with Bland + +## Step 1: Creating an Encrypted Key with your Twilio Credentials + +1. Go to your [Twilio Console](https://www.twilio.com/console) and get your Account SID and Auth Token. +2. Create an `encrypted_key` by [sending an API request](/api-v1/post/accounts) to Bland. + +This is the only time that your `encrypted_key` will be returned to you. Make sure to store it securely, and new keys will need to be generated if lost. + +## Step 2: Using the Encrypted Key in Outbound Calls + +Include `encrypted_key` in the headers (in addition to the `Authorization` header) of your API requests, and we'll use that account's credentials to make the call. + +For example: + +```json +{ + "Authorization": "BLAND_API_KEY", + "encrypted_key": "YOUR_ENCRYPTED_KEY" +} +``` + +Note: + +- You can set your `from` number in the API request - this will need to be a number owned by that Twilio account (and not one purchased through Bland). +- By default, we'll send calls from a randomly selected number in the specified Twilio account if a `from` is not specified. + +## Step 3: Uploading Inbound numbers + +1. Go to your [Twilio Console](https://www.twilio.com/console) and get your Twilio phone number(s). +2. Upload your numbers [through the API](/api-v1/post/inbound-insert). + +We'll validate that these numbers are owned by that account and add them to your Bland account. + +## Step 4: Configuring Inbound Numbers/Webhooks + +Note: When updating inbound numbers, the headers need to include the `encrypted_key` in addition to the `Authorization` header. Doing so makes sure the updates are applied to the correct Twilio account. + +Once you update an inbound number through the [Dev Portal](https://app.bland.ai) or [API](/api-v1/post/inbound-number-update), that number will be automatically configured to run on Bland's infrastructure. No additional steps are required! diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/enterprise-rate-limits.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/enterprise-rate-limits.mdx new file mode 100644 index 00000000000..7c1f7055bb9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/enterprise-rate-limits.mdx @@ -0,0 +1,11 @@ +--- +title: Enterprise rate limits +slug: enterprise-features/enterprise-rate-limits +--- + + +By default, Bland customers can dispatch 1000 calls/day before hitting rate limits. + +Enterprise customers start at 20,000 calls per hour, and 100,000 calls per day. + +To raise your rate limits or discuss limits larger than what's offered on enterprise, reach out here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/unlimited-support.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/unlimited-support.mdx new file mode 100644 index 00000000000..197a58141d9 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/enterprise-features/unlimited-support.mdx @@ -0,0 +1,15 @@ +--- +title: Unlimited support +slug: enterprise-features/unlimited-support +--- + + +Bland enterprise customers receive access to a shared slack channel with Bland's engineering team and founders. Enterprise customers also receive the founders phone numbers. Finally, for enterprise customers, the team strives to respond to every message in under five minutes, while maintaining a 24-hour SLA. + +Bland's engineers will guide on: + +1. Using features like `dynamic_data` and `custom_tools` +2. Best practices for prompting & configuring the phone agent +3. Making customizations (e.g. bring your own twilio, custom languages & voices, etc.) + +Reach out for enterprise access, here. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/featured-guides/bland-and-botpress.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/featured-guides/bland-and-botpress.mdx new file mode 100644 index 00000000000..45bbb7367bf --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/featured-guides/bland-and-botpress.mdx @@ -0,0 +1,25 @@ +--- +title: Bland & Botpress +slug: featured-guides/bland-and-botpress +--- + + +## Overview + +The integration process involves several key steps, starting with setting up your Botpress server and configuring your Bland AI account to ensure both platforms can communicate effectively. You'll learn how to authenticate your Botpress instance with Bland AI, send dynamic data (like phone numbers and intents) from chatbot conversations to Bland AI, and initiate voice calls based on user inputs or specific triggers within your chatbot workflows. **This guide is also a great way for no-coders to deploy a bland AI project easily, with little to no coding experience.** + +This guide will cover: + +1. Setting Up Botpress: Instructions on preparing your Botpress environment for integration, including installation and basic configuration. +2. Configuring Bland AI: How to set up your Bland AI account, obtain necessary API keys, and understand the API's capabilities related to voice interactions. +3. Integration Workflow: Step-by-step guidance on creating workflows in Botpress that interact with Bland AI's API, focusing on initiating voice calls to user-provided numbers. +4. Testing and Troubleshooting: Tips for testing your integrated solution to ensure it works as expected and troubleshooting common issues that may arise during the integration process. +5. Advanced Use Cases: Ideas and examples for leveraging this integration to create innovative chatbot applications that blend chat and voice interactions in unique and valuable ways. + +## Getting started + +[Read the entire guide](https://docs.google.com/document/d/1zn-89jYvpS238bQvp0XEfdV-s_txVlJIHrfIFhpNcJ4). + +## About Nort Labs + +Nort Labs is an agency that leverages AI technology to create bespoke marketing, design, and automation solutions for their customers. You can learn more about Nort's services by visitng their [website](https://nortlabs.com/). diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/fern.config.json b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/fern.config.json new file mode 100644 index 00000000000..2e3e1df85fd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/fern.config.json @@ -0,0 +1,4 @@ +{ + "version": "*", + "organization": "fern" +} \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo.png new file mode 100644 index 00000000000..6790583be04 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo/dark.svg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo/dark.svg new file mode 100644 index 00000000000..485b0131bcc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo/dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo/light.svg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo/light.svg new file mode 100644 index 00000000000..3334add5829 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/logo/light.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/send-phone-call.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/send-phone-call.png new file mode 100644 index 00000000000..f9d208f6f37 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/send-phone-call.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/starter-guide.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/starter-guide.mdx new file mode 100644 index 00000000000..e440d8b5c91 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/starter-guide.mdx @@ -0,0 +1,76 @@ +--- +title: Starter guide +subtitle: Learn how to use Bland's API in under five minutes +slug: starter-guide +--- + + +## Creating a developer account + + + +To get started, sign up on the developer portal. + +Enter your phone number and verification code. Finally, once your developer portal loads, go to the `Send phone call` page. + +## Sending your first phone call + + + +Although Bland is an API-first platform, the send phone call page provides a simple interface for quickly testing calls. On the left side you can adjust the call options and on the right hand side you can see how the code updates. + +Once you're satisfied with a call, copy the code on the right side (in Javascript, Python, or cURL) and add it to your application. + + + In the `Phone Number` field, enter your own phone number. + + For the task box either select one of the example prompts or write your own. For more instrucionts about prompting + your AI phone agent, read this blog post. + + + Scroll to the bottom of the page, and press the `Send call` button. Note, calls are charged at $0.12/minute, billed + to the exact second. + + + +To send a phone call programatically, read the API reference. + +## Testing your phone agent + + + +Once you've sent your first phone call, the next step is to test and improve the outputs from your phone agent. + +One way to test your agent is to send yourself test calls. A faster way, however, is to use the Bland AI testing suite. + + + + Select the model and language and insert your current prompt into the task box. + + + Start messaging your phone agent. Act like you're the person receiving the call, and purposefully ask edge-case + questions to throw the phone agent off. + + Based on the responses you receive, update the instructions in the prompt. + + +## Next steps + +You now know how to send and test phone calls, but you've only scratched the surface of Bland's capabilties. + +Areas for further exploration: + + + + Read the API reference. + + + Creating custom tools for interacting with external APIs, live, during phone calls. + + + Learn how to prompt the exact behavior you want from your phone agent. + + + See what people are building on Bland and get support from other users. + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/add_global_prompt.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/add_global_prompt.png new file mode 100644 index 00000000000..87a1b9d09d4 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/add_global_prompt.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/batchcalls.jpg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/batchcalls.jpg new file mode 100644 index 00000000000..bec46d668b1 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/batchcalls.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/condition_eg.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/condition_eg.png new file mode 100644 index 00000000000..db7e9f8302a Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/condition_eg.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/custom-tools.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/custom-tools.mdx new file mode 100644 index 00000000000..6e139477fc2 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/custom-tools.mdx @@ -0,0 +1,317 @@ +--- +title: Custom Tools +subtitle: Interact with the real world mid-call using custom tools. +slug: tutorials/custom-tools +--- + + +## Introduction + +Custom tools allow your agent to interact with any web API mid-call. Do things like: + + + + Dispatch SMS or emails using the person's contact info. + + + Set appointments using live calendar availability. + + + Generate support tickets in your issue tracker. + + + Update your CRM with relevant details during the call. + + + +## Background + +To understand how custom tools work, let's take a peek under the hood of the Bland AI phone agent. + +During the conversation, the phone agent is constantly listening to figure out when it's supposed to respond. When the phone agent realizes it's time to respond, it reviews the tools in its toolbox and picks between them. + +Those tools include a `speak`, `wait`, and `button press` tool. When you create a custom tool, you add it to the existing 'toolbox' for the phone agent to pick from. + +A few natural questions arise: + +1. How do I define my custom tool? +2. How do I make sure my tool gets picked at the right time? +3. How does information from the call get passed to my custom tool's API request? +4. How do I fill the silence (when my custom tool is running)? +5. How does the response from my custom tool get added to the call? + +Keep reading to find out. + +# Creating your custom tool + +## Defining the API request + +Imagine you're creating an AI phone agent to take restaurant orders. You want your phone agent to have the ability to place orders, by pinging your backend API. + +Here's what that request might look like: + +```json +{ + "method": "POST", + "url": "https://api.your-restaurant.com/orders", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer YOUR_API_KEY" + }, + "body": { + "items": [ + { + "name": "burger", + "patties": 2, + "quantity": 1 + }, + { + "name": "fry", + "size": "lg", + "quantity": 1 + } + ] + } +} +``` + +## From API request to custom tool + +The next step is to convert the API request into a custom tool. Custom tools have the following properties: + +- `name` - the agent will see this in the list of tools +- `description` - a short explanation of what the tool does +- `input_schema` - a JSON schema describing the input data +- `speech` (optional) - a string that will be spoken to the agent while your tool waits for a response +- `response_data` - An array of objects that describe how to extract data from the response. Within the response data, you can create variables that the phone agent can reference in its prompt. + +### Name & Description + +The agent will see the name in the list of tools. The name, plus the description, help the AI phone agent when it decides which tool to use. + +For this example we'll set the name to `PlaceOrder`, and the description to `Places the final order for the drive thru`. + +### Input Schema + +The input schema is critical. It defines the shape of the API request, the different inputs the request can take, and also includes an example (which helps our system when creating requests). + +Here's what the input schema could look like: + +```json +"input_schema": { + "example": { + "items": [ + { + "name": "burger", + "patties": 2, + "quantity": 3 + }, + { + "name": "fry", + "size": "lg", + "quantity": 2 + } + ] + }, + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "patties": { + "type": "integer" + }, + "size": { + "type": "string", + "enum": [ + "sm", + "md", + "lg" + ] + }, + "flavor": { + "type": "string", + "enum": [ + "chocolate", + "strawberry", + "vanilla" + ], + "default": "vanilla" + }, + "quantity": { + "type": "integer" + } + }, + "required": [ + "name", + "quantity" + ] + } + } + } +} +``` + +Two important notes about input schema: + +1. `input_schema` is converted into the variable `"{{input}}"` that you can use in the request body/query/headers +2. To access nested properties, use dot notation: `"{{input.property.subproperty}}"` + +Scroll down to see the full example. + +### Speech + +Because requesting external APIs might take a while, we enable you to define a `speech` property. The phone agent will say the `speech` while it makes the request. + +An example speech might look like: `Perfect, I'll schedule that right now, give me just a second.` + +For the restaurant ordering example, the speech could be `Thank you, placing that order now.` + +### Response data + +Once your API request comes back, you need to extract the response data, and then make the phone agent aware of the new information. + +The `data` field determines how you extract the data while the `name` field determines the variable name for reference in the prompt. + +Here's an example response data: + +```json +"response_data": [ + { + "name": "order_price", + "data": "$.price" + } +] +``` + +## Full example + +Below is the entire API request for sending a phone call using the outlined custom tool: + +```json +{ + "phone_number": "...", + // note that the returned value ({{order_price}}) in the task is populated only after the tool is run + "task": "You are taking a drive thru order from a customer. Find out everything that they want like a drive thru cashier. Continue until they say they're done. Repeat the full order back to them after that, and ask if that's correct. If they confirm that it's correct, then and only then will you place their order using the PlaceOrder tool. After you place it, tell them their order total and to pull forward. Their order price is {{order_price}}", + "first_sentence": "Hi, what can I get started for you today?", + "request_data": { + "menu": { + "burger": ["patties"], + "fry": ["size"], + "shake": ["flavor", "size"] + } + }, + "tools": [ + { + "name": "PlaceOrder", + "description": "Places the final order for the drive thru.", + "speech": "Thank you, placing that order now.", + "input_schema": { + "example": { + "items": [ + { + "name": "burger", + "patties": 2, + "quantity": 3 + }, + { + "name": "fry", + "size": "lg", + "quantity": 2 + } + ] + }, + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "patties": { + "type": "integer" + }, + "size": { + "type": "string", + "enum": ["sm", "md", "lg"] + }, + "flavor": { + "type": "string", + "enum": ["chocolate", "strawberry", "vanilla"], + "default": "vanilla" + }, + "quantity": { + "type": "integer" + } + }, + "required": ["name", "quantity"] + } + } + } + }, + "url": "https://api.your-restaurant.com/orders", + "method": "POST", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer ..." + }, + "body": { + "items": "{{input.items}}" + }, + "response_data": [ + { + "name": "order_price", + "data": "$.price" + } + ] + } + ] +} +``` + +# Frequently asked questions + + + + The phone agent refers to the input schema and the `example` within it. Both of those pieces of information provide context about what data to pass. + + Then the phone agent extracts the information from the transcript and passes it to the request body. + + You can improve the accuracy of the input data by creating a very clear `input_schema`. That includes providing a detailed `example` within. + + + +{" "} + + + The phone agent looks at the tool's name and description. Then it looks at the current context of the conversation to + decide whether using the tool makes sense. + + + + When the API response from the custom tool comes back, you can extract the API response and create variables. You can do this within the `response_data` property. + + Once you've given the variable a `name`, you can reference it in the prompt using double brackets (`{{}}`). + + Note, with the current setup, you might reference variables that have null values. Here's the restaurant example prompt: + + You are taking a drive thru order from a customer. Find out everything that they want like a drive thru cashier. Continue until they say they're done. Repeat the full order back to them after that, and ask if that's correct. If they confirm that it's correct, then and only then will you place their order using the PlaceOrder tool. After you place it, tell them their order total and to pull forward. Their order price is `{{order_price}}` + + In the above prompt, the `order_price` will be null until the data comes back. That's okay though. The prompt is structured to first take the order, then use the PlaceOrder tool, and then finally respond with the order price. + + By the time the phone agent is asked for the order price, it will have the information. + + Custom tools will continue getting more robust, to further prevent scenarios where variables without value from being referenced in prompts. + + + + +If you have any additional questions, reach out at hello@bland.ai and one of our engineers will help. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/default_node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/default_node.png new file mode 100644 index 00000000000..a40ca317443 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/default_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/dynamic-data.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/dynamic-data.mdx new file mode 100644 index 00000000000..c05acc573af --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/dynamic-data.mdx @@ -0,0 +1,270 @@ +--- +title: Dynamic Data +subtitle: Interact with the real world by connecting your agent to external APIs. +slug: tutorials/dynamic-data +--- + + +## Introduction + +With Dynamic Data, you can make external API requests at the start and throughout your phone call. This allows you to load data from your database, or from any other API. You can then use that data in your AI responses, or to define circumstantial behavior for each call. + +Some examples of what Dynamic Data enables: + +- Maintain conversation history between calls +- Define behavior based on the user's location +- Handle real-time data like status updates or prices + + + +Here's how to make an [Inbound Agent](/api-v1/post/inbound-number-update) remember past conversations they've had with callers. There's a lot of other useful information we can integrate, like how long ago that call occurred and the call's duration. + +`Endpoint: POST https://api.bland.ai/v1/inbound/<>` + +```json +{ + "prompt": "You're a service agent working on behalf of Bland AI...", + "dynamic_data": [ + { + // First, retrieve the previous call's data + "url": "https://api.bland.ai/v1/calls", + "method": "GET", + "headers": { + "authorization": <> + }, + "query": { + // These parameters narrow down the search and will make the request faster + // as well as easier to understand during later analysis. + "from_number": "{{phone_number}}", + "from": 1, // Offset by 1 to exclude the current call + "limit": 1 + }, + "response_data": [ + { + // These are the variables you're defining + "name": "previous_call_id", + // And the path to the data you want to extract + "data": "$.calls[0].c_id" + }, + { + "name": "previous_call_time", + "data": "$.calls[0].created_at" + }, + { + "name": "previous_call_duration_minutes", + "data": "$.calls[0].call_length" + } + ] + }, + { + // Once we have the previous call's ID, we can retrieve the transcript + // Note the variable used in the URL + "url": "https://api.bland.ai/v1/calls/{{previous_call_id}}", + "method": "GET", + "headers": { + "authorization": <> + }, + "response_data": [ + { + "name": "previous_conversation", + "data": "$.concatenated_transcript", + // The context parameter elaborates on the variable's purpose and use + "context": "Your previous conversation with this person (if it exists): {{previous_conversation}}" + } + ] + }, + { + // Helpful tip for debugging: + // You can send the data to a webhook to see what it looks like + "url": "https://webhook.site/...", + "method": "POST", + // Setting cache to false means you'll be able to see the data change in real-time + "cache": false, + "body": { + // And here's the data we're sending + "call_id": "{{call_id}}", + "prev_call_id": "{{previous_call_id}}", + "now": "{{now}}", + "previous_call_time": "{{previous_call_time}}", + "previous_conversation": "{{previous_conversation}}" + } + // Additional note: + // Since no response_data is defined, latency isn't affected by this request + } + ] +} +``` + + + +We'll cover the following features in this section: + +- System variables +- External API requests +- Extracting data from responses +- Variables as parameters +- Chaining requests + +## System Variables + +Variables are defined with double curly braces, like `{{variable}}`. System variables are predefined variables that are available in every AI phone call. You can use them to access information about the current call, like the user's phone number or the current time. + +Note: Variables are NOT case sensitive, and outer spaces are trimmed automatically. + +Base variables: + +- `{{phone_number}}` - Always the other party's number +- `{{country}}` - The country code (ex. US) +- `{{state}}` - The state or province's abbreviation (ex. CA for California) +- `{{city}}` - The full city name, capitalized +- `{{zip}}` - The zip code +- `{{call_id}}` - The unique ID of the current call +- `{{now}}` +- `{{now_utc}}` +- `{{from}}` - The outbound number in E.164 format +- `{{to}}` - The inbound number in E.164 format +- `{{short_from}}` - outbound number with country code removed +- `{{short_to}}` - inbound number with country code removed + +Variables can be used mid-sentence, like this: + +```json +{ + "task": "... Today is {{now}} ..." +} +``` + +## External API Requests + +External API requests are defined in the `dynamic_data` parameter of your call request or inbound agent configuration. The `dynamic_data` parameter is an array of objects, where each object represents an API request. + +Here's a simple request that can be used to load public data about the current price of Bitcoin, then store it in a variable called `{{bitcoin_price}}`: + +```json +{ + "dynamic_data": [ + { + "url": "https://api.coindesk.com/v1/bpi/currentprice.json" + "response_data": [ + { + "name": "bitcoin_price", + "data": "$.bpi.USD.rate", + "context": "Current price of Bitcoin in USD is ${{bitcoin_price}}" + } + ] + } + ] +} +``` + + + - `timeout` - The maximum number of milliseconds to wait for a response. - Default: `2000` - `method` - The HTTP + method to use. - Defaults to `GET`, otherwise `POST` is allowed. - `headers` - An object of headers to send with the + request. - `body` - The body of the request. Only used if `method` is `POST`. - `response_data` - An array of objects + that define how to extract data from the response. - See the next section for more details. - `cache` - Whether to + store the response, or refresh that data before each AI response. - Defaults to `true`. + + +## Extracting Data from Responses + +Rather than using the full response, you can extract specific data from the response using the `data` parameter. The `data` parameter follows JSON structuring, using dot notation and array indices. For example, if the response is: + +```json +{ + "bpi": { + "USD": { + "code": "USD", + "rate": "9,000.00", + "description": "United States Dollar", + "rate_float": 9000 + }, + "GBP": { + "code": "GBP", + "rate": "6,000.00", + "description": "British Pound Sterling", + "rate_float": 6000 + } + } +} +``` + +Then `$.bpi.USD.rate` would return `9,000.00`. + +More complex filters can be used if they follow the [JSONPath format](https://docs.hevodata.com/sources/engg-analytics/streaming/rest-api/writing-jsonpath-expressions). + +## Variables as Parameters + +Once defined with `response_data`, variables can be used nearly anywhere. + +- In the `task` or `prompt` parameters +- In the `context` parameter of `response_data` +- In the `body`, `headers` and/or `query` parameter of each request + +Afterwards, in your webhooks and when retrieving call transcripts, the `variables` field will contain all variables that were defined during the call. + +By far, the easiest way to test out your `dynamic_data` configuration is via the [/dynamic_data/test](/api-reference/endpoint/dynamic_validate) endpoint. It returns the original configuration, every raw response, and the final variables after parsing is applied. + +## Chaining Requests + +Each request is executed in order, and variables defined in previous requests can be used in the next request. For example, if you want to retrieve information from your database or ours, then take additional actions with that data then you could do something like the following: + +For this example, imagine a delivery service that offers instant checkout for customers that have signed up to be a member. The first request retrieves their member_id from your database like so: + +```json +{ + "dynamic_data": [ + { + "url": "https://api.restaurant.com/customers", + "method": "GET", + "headers": { + "authorization": "ExtremelySecureCredentials" + }, + "query": { + "phone_number": "{{phone_number}}" + }, + "response_data": [ + { + "name": "member_id", + "data": "$.customer.member_id" + } + ] + } + //... + ] +} +``` + +We just created that `{{member_id}}` variable - now we can use it in the next request. + +This delivery service also can be called to check on an order status. + +Note a difference: The `cache` parameter is set to `false`, so if the order status changes during the call, the agent will immediately know about it and be able to inform the customer. + +```json +{ + "dynamic_data": [ + //... + { + "url": "https://api.restaurant.com/orders", + "method": "GET", + "cache": false, + "headers": { + "authorization": "ExtremelySecureCredentials" + }, + "query": { + "member_id": "{{member_id}}" + }, + "response_data": [ + { + "name": "order_id", + "data": "$.orders[0].id" + }, + { + "name": "order_status", + "data": "$.orders[0].status" + } + ] + } + ] +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/end_node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/end_node.png new file mode 100644 index 00000000000..4a0c2d395d2 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/end_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/extract_variables.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/extract_variables.png new file mode 100644 index 00000000000..14f1530e29f Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/extract_variables.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/final_pathway.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/final_pathway.png new file mode 100644 index 00000000000..9f592f20658 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/final_pathway.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/finetune.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/finetune.png new file mode 100644 index 00000000000..fc5d7d3f425 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/finetune.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/finetuned.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/finetuned.png new file mode 100644 index 00000000000..de014643589 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/finetuned.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/global_eg.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/global_eg.png new file mode 100644 index 00000000000..31436694447 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/global_eg.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/global_node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/global_node.png new file mode 100644 index 00000000000..40eb7a91fa9 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/global_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/inbound-number-webhook.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/inbound-number-webhook.png new file mode 100644 index 00000000000..7b3b9e0d406 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/inbound-number-webhook.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/kb_node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/kb_node.png new file mode 100644 index 00000000000..d496d9cc009 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/kb_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/live-transfer.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/live-transfer.mdx new file mode 100644 index 00000000000..148560aea01 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/live-transfer.mdx @@ -0,0 +1,85 @@ +--- +title: Live transfer +slug: tutorials/live-transfer +--- + + + + +## Introduction + +Implementing live transfer in your AI-powered phone calls enhances flexibility and customer experience. This guide will explain how to set up a live transfer during a call using Bland AI. + +## Step 1: Understand Live Transfer + +Live transfer allows the AI agent to transfer the call to a human representative under certain conditions. This is crucial for scenarios where human intervention is preferred. + +## Step 2: Setup Your Authorization + +Before initiating a live transfer, ensure your API key is ready. Obtain your key from the [developer portal](https://app.bland.ai) if you haven't already. + +## Step 3: Configure the Transfer Settings + +Include the `transfer_phone_number` parameter in your call data. This is the number the AI will transfer to. Additionally, define the conditions for transfer in the task parameter. + +Example: + +```javascript +{ + "phone_number": "+11233456789", + "transfer_phone_number": "+19876543210", + "task": "If the caller requests to speak with a human, transfer the call to the representative." +} +``` + +## Step 4: Send the API Request + +Make the API request using the JavaScript or Python code snippet provided, ensuring the `transfer_phone_number` and conditions are included. + +## Step 5: Test and Monitor + +After setting up the live transfer, test the functionality to ensure it works as expected. Monitor the calls to adjust the transfer conditions as necessary. + +## Conclusion + +Setting up a live transfer offers a seamless experience for situations where AI needs to hand over to a human. You're now ready to integrate this feature into your AI-powered calls with Bland AI. + +Maintain a balance between automated and human interactions to optimize customer satisfaction. Happy calling! + + + +```javascript LiveTransfer.js +// Headers +const headers = { + authorization: "YOUR-API-KEY-HERE", +}; + +// Data +const data = { + phone_number: "+11233456789", + transfer_phone_number: "+19876543210", + task: "If the caller requests to speak with a human, transfer the call to the representative.", +}; + +// API request +await axios.post("https://api.bland.ai/v1/calls", data, { headers }); +``` + +```python LiveTransfer.py +# Headers +headers = { + 'authorization': 'YOUR-API-KEY-HERE' +} + +# Data +data = { + 'phone_number': '+11233456789', + 'transfer_phone_number': '+19876543210', + 'task': 'If the caller requests to speak with a human, transfer the call to the representative.' +} + +# API request +response = requests.post('https://api.bland.ai/v1/calls', json=data, headers=headers) +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/live_call_logs.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/live_call_logs.png new file mode 100644 index 00000000000..bfa24e22ab4 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/live_call_logs.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/livetransfer.jpg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/livetransfer.jpg new file mode 100644 index 00000000000..63b16780d19 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/livetransfer.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/max-duration.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/max-duration.mdx new file mode 100644 index 00000000000..73093001c72 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/max-duration.mdx @@ -0,0 +1,87 @@ +--- +title: Setting max duration +slug: tutorials/max-duration +--- + + + + +## Introduction + +Controlling the length of your AI-powered phone calls is crucial for efficiency and cost-effectiveness. In this guide, we'll walk you through how to set a `max_duration` for your calls using Bland AI. + +## Step 1: Understand max_duration + +The `max_duration` parameter allows you to specify the longest duration you want your call to last, measured in minutes. Once the set duration is reached, the call will automatically end. + +## Step 2: Setup Your Authorization + +Ensure you have your API key ready for authentication. If you haven't obtained one, sign up on the [developer portal](app.bland.ai). + +## Step 3: Define max_duration in Your Call Data + +When preparing your call data, include the `max_duration` parameter. You can set it as a float or a string. + +Example values: `"30", "5.5", 45, 2.8`. + +Here’s how you might include it in your request data: + +```javascript +{ + "phone_number": "+11233456789", + "task": "Inquire about the latest product updates.", + "max_duration": "10", // Call lasts for 10 minutes max +} +``` + +## Step 4: Send the API Request + +Use the provided JavaScript or Python code snippet to make the API request, ensuring you include the `max_duration` parameter. + +## Step 5: Test and Adjust + +After setting `max_duration`, test your calls to ensure they're ending at the desired time. Adjust as necessary based on your needs and feedback. + +## Conclusion + +Setting a `max_duration` for your calls ensures they are concise and to the point, saving time and resources. You're now ready to efficiently manage the length of your AI-powered calls with Bland AI. + +Remember, the key is to find the right balance between giving enough time for meaningful interaction and keeping the calls concise. Happy calling! + + + +```javascript SendCall.js +// Headers +const headers = { + authorization: "YOUR-API-KEY-HERE", +}; + +// Data +const data = { + phone_number: "+11233456789", + task: "Inquire about the latest product updates.", + max_duration: "10", // Call lasts for 10 minutes max +}; + +// API request +await axios.post("https://api.bland.ai/v1/calls", data, { headers }); +``` + +```python SendCall.py +# Headers +headers = { + 'authorization': 'YOUR-API-KEY-HERE' +} + +# Data +data = { + 'phone_number': '+11233456789', + 'task': 'Inquire about the latest product updates.', + 'max_duration': '10', # Call lasts for 10 minutes max +} + +# API request +response = requests.post('https://api.bland.ai/v1/calls', json=data, headers=headers) +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/maxduration.jpg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/maxduration.jpg new file mode 100644 index 00000000000..648a1b301bb Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/maxduration.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/new_pathway.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/new_pathway.png new file mode 100644 index 00000000000..e6255897d8d Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/new_pathway.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/node.png new file mode 100644 index 00000000000..6bc933e0020 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathway_chat.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathway_chat.png new file mode 100644 index 00000000000..9467b610533 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathway_chat.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathway_label.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathway_label.png new file mode 100644 index 00000000000..1d7654a379a Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathway_label.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathwaylog_finetune.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathwaylog_finetune.png new file mode 100644 index 00000000000..b6df97a0c05 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathwaylog_finetune.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathways.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathways.mdx new file mode 100644 index 00000000000..d8876440ac8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/pathways.mdx @@ -0,0 +1,289 @@ +--- +title: Conversational Pathways +subtitle: >- + Gain greater control over your AI agent and the conversational flow. [Create a + Pathway Now!](https://app.bland.ai/dashboard?page=convo-pathways) +slug: tutorials/pathways +--- + + +[Template Pathway Video Tutorial](https://www.loom.com/share/be9d6f1072ae4267abc0717e36e66078?sid=82a7843f-9e7c-457a-8f7b-4d8ca026e1ff) + +## Introduction + +Conversational pathways are our new way of prompting Bland that has led to major breakthroughs in realism. + + + + Give your agent instructions on how it should respond at specific points of the conversation. Choose between + prompting or fixed sentences. + + + Execute webhooks at any point during the conversation, and send speech during/after the webhook. + + + Control when your agent ends the call + + + Connect your agent to a knowledge base, to answer any questions the user has. + + + +# Terminologies + +To understand how pathways work, let's first understand the terminologies. + +## Nodes + +These blocks you see here are called Nodes. + + + +## Pathways + +Each of these dotted lines is called a Pathway. Their start end end points are the `Purple Circles` on the top and bottom of the nodes. + +In order to create a pathway from a node, you would click on the purple circle at the bottom of the node and drag your mouse to connect to the top purple circle another node. + +Upon doing so, you will now have a new dotted line connecting the two nodes, with a 'New Pathway' button in the middle of the line. + + + +In order to instruct the agent when to take this pathway, you would click on the Edit icon on the 'New Pathway' button, and input the conditions for when the agent should take this pathway. In the above example, the agent would take this pathway if the user is not available to talk, so I labelled the pathway as 'not a good time to talk'. + + + +And there you have it! You have now created a pathway from one node to another, and instructed the agent when to take this pathway. You can connect as many nodes as you want in this manner, and create as many pathways as you want. In order to create a new Node, press the 'Add new Node' button at the top-left of the screen. + +# How the Pathways Agent Works + +The agent starts at the first node, and then moves to the next node based on the pathway that the agent decides to take. The agent will then execute the instructions in the node as dialogue, and then move on to the next node based on the pathway that the agent decides to take. This process will continue until the agent decides to end the call. + +The agent will make decisions based on the labels you put in the pathways, when connecting one node to another, and the dialogue generated will be based on the instructions you set in the nodes. + + + +For Example, + +In this example, at the node named 'Ask for reservation info', the node asks for the user's reservation information. Based on the user's response, the agent will then move on to the next node based on the labels you put in the pathways. For the current node, it will check if the user has provided reservation information where the number of guests is either less than 8 or more than 8. If the user has provided reservation information where the number of guests is less than 8, the agent will move on to the node named 'Reservation booking'. If the user has provided reservation information where the number of guests is more than 8, the agent will move on to the node named 'Transfer Call'. The agent will then execute the instructions in the node as dialogue, and then move on to the next node based on the pathway that the agent decides to take. And the process repeats! + +## Conditions + +Conditions are a way to provide the agent with a condition that must be met in order for the agent to move on to the next node. If the condition is not met, the agent will stay on the same node and ensure the condition is met until the condition is fulfilled. + +Using the same example above, I set the condition for the 'Ask for reservation info' node as follows - "You must get the date, time, and number of guests for this reservation". This means that the agent will stay on the 'Ask for reservation info' node until the user provides the date, time, and number of guests for the reservation. If the user says something else or deviates from the conversation, the agent will stay on the 'Ask for reservation info' node and prompt the user to provide the date, time, and number of guests for the reservation. + +This helps you to ensure that the user provides the necessary information before moving on to the next node, and helps you to control the flow of the conversation. + + + +## Global Nodes + + + +Global Nodes take precendence over the condition decisions made by the agent. You can treat a global node as a node, that every other node in the pathway has a pathway to, with the label as the 'Global Pathway Label'. + +Using the Reservation Booking Example, if the user were to ask a question like 'What are the opening hours of the restaurant' when the agent is at the 'Ask for reservation info' node, the condition decision would not be met as the user did not provide the date, time, and number of guests for the reservation. However, the pathway label would be 'user has a question about the restaurant's hours or location', which links to a Global Node. As Global Nodes take precedence over the condition decision, the agent would then move to the 'Global Node' named 'Restaurant Questions' and provide the user with the opening hours of the restaurant. After providing the user with the opening hours of the restaurant, the agent would then automatically return to the 'Ask for reservation info' node, and continue with the flow of the conversation. + +This helps you to handle edge cases where the user might ask a question that is not related to the current conversation, and allows you to provide the user with the information they need, before returning to the conversation. + +Tip: The variables `{{lastUserMessage}}` and `{{prevNodePrompt}}` can be used in the Global Node to provide the agent with context on what the user said, and steering the conversation back to its own original goal. + + +You are to answer any questions the user has, to the best of your knowledge. If you do not know the answer, simply say 'I don't have that information at that moment'. Do not make up any information/facts about the appointment. + +After you answer the question, you are to direct the conversation back to your initial goal, which was as follows: + +Previous Goal: +`{{prevNodePrompt}}` + +If you deem the goal as achieved, you can simply ask the user 'So, shall we proceed?' . If the goal has not been achieved, you are to steer the conversation back to achieve your goal. +If you deem the goal as achieved, simply confirm the result with the user. If the goal has not been achieved, you are to steer the conversation back to achieve your goal. + +``` +Examples +-- +assistant: what date works for you? +user: what day is it today? +assistant: The day today is April 15th. So, what date works for you? +-- + +-- +assistant: what date works for you? +user: probably tomorrow, what time does the clinic open? +assistant: The clinic opens at 9am. So, do you want to schedule for tomorrow? +-- +``` + + + + + +# Node Types + +There are currently 6 different types of Nodes + +- Default +- Webhook +- Knowledge Base +- End Call +- Transfer Call +- Wait for Response + +You can select the type of node you want by clicking on the dropdown of `Node Type`. + +## Base/Default Node (Important!) + +The default node provides the ability to generate a response to the user. This functionality is exposed to all other nodes as well. + +You can either: + +- Use the `Prompt` field to give instructions on what the agent should do at this point in the conversation. This is the recommended way to generate responses as it makes the conversation more human and natural. +- Enable the 'Static Text' toggle to provide a fixed response, and the agent will always say the same thing at this point in the conversation. + + + +### Optional Decision Guide + +The optional decision guide is only to be used if your phone agent currently is not going down the correct pathway. We do not expect this to happen often, but still want to provide you the tools to handle these issues if they arise. + +It is a way to provide the phone agent with example scenarios of what the user might say, and what pathway the agent should take in response. + +You would put in examples of what the user might say in the `User Input` field, and then in the `Pathway` field, you would provide the pathway the agent should take in response. + +### Condition + +The condition is a way to provide the agent with a condition that must be met in order for the agent to move on to the next node. If the condition is not met, the agent will stay on the same node and ensure the condition is met until the condition is fulfilled. + +### Global Nodes + +Each node can be configured to be a Global Node. Global Nodes are nodes that are accessible by every other node in the Conversational Pathway. This means that it has an implicit pathway to every other node, and the label would be the 'Global Label'. + +After entering a Global Node, the agent will execute the instructions inside the Global Node, and then automatically return to the node it was at before entering the Global Node, so it can continue with the flow of the conversation. + +In a Global Node, you can also forward the agent to another node, by toggling the 'Enable Forwarding', which will allow you to select the node you want to move the agent to. This is useful if you want to move the agent to a existing node for a certain scenario, which could happen at any point in the conversation. + + + +## Transfer Call Node + +The transfer call node is used to transfer the call to another number when the node is reached, and the dialogue at this node is complete. + +As such, you may have the agent say any final words before the call is transferred. + + + +## End Call Node + +The end call node will end the call when the node is reached, and the dialogue at this node is complete. + +As such, you may have the agent say any final words before the call is ended. + + + +## Knowledge Base Node + +The knowledge base node is used to connect your agent to a knowledge base, to answer any questions the user has. + +Paste in any text in the 'Knowledge Base' field, and the agent will search through the knowledge base to answer the user. + +Coming soon - PDF Upload/Vector Database Integrations... + + + +## Wait for Response Node + +The Wait for Response Node works the same way as the Default Node, except it is also equipped with the ability to wait if the user requires time to respond or needs to hold for a moment. + +## Webhook Node + +The webhook node is used to execute webhooks at any point during the conversation, and send speech during/after the webhook. + +`Webhook Information` is all you need in order to execute a webhook, and works the same way as Dynamic Data. Refer to the [Dynamic Data](https://docs.bland.ai/tutorials/dynamic-data) section for more information... + +Similar to how the dialogue is handled in all other nodes, you can control the dialogue sent before, and after the webhook is executed. + +Variables received from the webhook can be used in the dialogue as well, as shown in the example below. + + + +# Global Prompt for all Nodes + +The 'Add Global Prompt to All Nodes' feature is to assist in providing context/instructions to the agent for all nodes, without having to manually input the same prompt for each node. One Example of a Global Prompt could be to provide the agent with instructions on how to handle the call, the tone of voice to use, or answering any questions the user might have. + + + +# Live Call Logs + +The live call logs are a way to provide you with a live feed of the conversation between the agent and the user. This is useful for debugging purposes, and to see how the agent is responding to the user in real-time and the decisions the agent is making. On top of the transcript, we expose the updated node the agent took, the pathway that the agent took, as well as whether the condition was met or not. + + + +# Testing the Pathway Agent via Chat + +You can test the responses from your pathway agent by clicking on the 'Chat with Pathway' button at the top right of the screen. This will open a chat window where you can test the agent by sending messages to the agent, and see how the agent responds. The live call logs will also be displayed on the right side of the screen, so you can see the decisions the agent is making in real-time. + + + +# Variables Reference/ Extraction + +## Variables + +You can reference variables in pathways using double curly braces like `{{first_name}}`. You can pass variables into your call by passing in the key-value pairs in the `request_data` field when sending a call. + +Some examples of variables that you can access at each node: + +- `{{lastUserMessage}}` - The last response from the user +- `{{prevNodePrompt}}` - The prompt from the previous node +- `{{now_utc}}` - The current time in UTC +- `{{from}}` - The phone number the call is from +- `{{to}}` - The phone number the call is to +- `{{call_id}}` - The unique identifier for the call + +## Extracting Variables from Call/User Response + +At each node, you can also extract variables from the user's response using the `Extract Variables from Call Info` field. You would put in the name of the variable you want to extract, the type of the variable (integer, string, boolean), and the description for what information you want the variable to store. You can also provide specific formats you expect and examples in the description. The more descriptive it is, the better the agent will be at extracting the variable more accurately. + +Do note that when enable variable extraction, and wanting to reference the variable in the subsequent nodes, it would introduce slight latency as the agent would have to extract the variable from the user's response before generating the dialogue for the next node. + +Within the webhook node, the variable extraction happens before the webhook is executed, so that you can reference the variables extracted from the user's response in the webhook's request data. The variables extracted from the webhook in `response_data` can also be referenced in the dialogue generated after the webhook is executed, in the same manner. + +For Example, the image below shows how the Webhook Node is set up to extract the date, time, and number of guests for the reservation, and how the extracted variables are referenced in the webhook's request data. + + + +# Fine Tuning the Pathway agent + +While Conversational Pathways gives you a lot greater control compared to the regular call agent, hallucinations can still occur, or the agent might make wrong decisions in the pathway. However, we have provided you with the tools to handle these issues if they arise. Each node can be fine-tuned on the decision it makes, as well as the expected dialogue it generates, allowing you to handle even the most extreme edge cases that might arise easily. + +### Steps to Fine-Tuning the agent + +Upon triggering and testing a call with your pathway using the 'Chat with Pathway' button, or sending a call, you will be able to see the live call logs. + +If you see a decision that the agent made that you do not agree with, or if the agent is hallucinating, you can fine-tune the decision the agent makes by clicking on the `Edit` button on the `PATHWAY DECISION INFO` block. + + + +Upon doing so, you will be able to see the decisions the agent made for the condition, the pathway it took, and the dialogue it generated. You can then fine-tune the agent by changing the dialogue, the condition, or the pathway the agent took. + +Upon saving your changes, you will be able to see the fine-tuning data in the node where the decision was made. This training data is used to train the agent to make better decisions in the future, and to ensure that the agent does not make the same mistake again. + + + +Do note that the decisions for the condition and pathway chosen is made by the node the agent is currently at, and the dialogue is generated by the node which the agent decides to take the pathway to. + +For Example, +If the agent is at Node 1, and the agent decides that the condition is achieved, and decides to take the pathway to Node 2, the dialogue generated will be from Node 2, and the decisions for the condition and pathway chosen will be from Node 1. As such, the training data for the condition and pathway chosen will be stored in Node 1, and amendments to the dialogue generated will be stored in Node 2. + + + +# Quick Start with Tutorial / Templates + +To immediately start playing around with Conversational Pathways, you can use one of our templates. Visit the Conversational Pathways page on our Dev Portal, and duplicate the 'Restaurant Reservation' Template, and run through the agent to see how it works! +We have also created a video walkthrough of that template to help you get started! + +[Video Walkthrough/Tutorial](https://www.loom.com/share/be9d6f1072ae4267abc0717e36e66078?sid=82a7843f-9e7c-457a-8f7b-4d8ca026e1ff) + +Create a pathway now! Click [here](https://app.bland.ai/dashboard?page=convo-pathways) to get started! + +If you have any additional questions, reach out on our [Discord](https://discord.gg/QvxDz8zcKe) and one of our engineers will help. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/send-1000-calls-at-once.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/send-1000-calls-at-once.mdx new file mode 100644 index 00000000000..4ff9d56bb0c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/send-1000-calls-at-once.mdx @@ -0,0 +1,116 @@ +--- +title: Send 1000 phone calls +slug: tutorials/send-1000-calls-at-once +--- + + + + +## Step 1: Setup Your Authorization + +To initiate a batch call, you must first authenticate your request. Ensure you have your API key from signing up on the [developer portal](https://app.bland.ai). + +## Step 2: Create the Base Prompt + +Craft a base prompt that will be common across all calls in the batch. Use placeholders `{{curly braces}}` for dynamic content. + +Example: + +```javascript +"You are calling {{business}} to renew their subscription to {{service}} before it expires on {{date}}."; +``` + +## Step 3: Define the Call Data + +Specify the list of calls in the `call_data` array. Each call must have a `phone_number` and can include other properties corresponding to placeholders in your base prompt. + +Example: + +```javascript +[ + { + phone_number: "1234567890", + business: "ABC co.", + service: "Netflix", + date: "September 4th", + }, + { + phone_number: "32176540987", + business: "XYZ inc.", + service: "Window Cleaning", + date: "December 20th", + }, +]; +``` + +## Step 4: Additional Configuration + +- `label`: Assign a label to your batch for easy tracking. +- `campaign_id`: Organize related batches under a campaign. +- `test_mode`: Set to true for testing with the first call only. +- `batch_id`: Manually set or auto-generated for tracking. +- Voice and Language Settings: Select `voice_id`, `reduce_latency`, and `language`. +- `request_data`: Include specific facts for the AI to know during the call. +- `webook`: For real-time notifications and transcripts post-call. +- `max_duration`: Define the maximum length of each call. +- `amd`: Enable for navigating phone trees or leaving voicemails. +- `wait_for_greeting`: Control if the AI speaks immediately or waits. + +## Step 5: Send the API Request + +Use the provided JavaScript or Python code snippet to make the API request. + +## Step 6: Handle the Response + +After sending the batch request, you'll receive a response with a `message` and the `batch_id`. Monitor the progress of your calls and any responses via your specified webhook. + +Here's what an example response might look like: + +```javascript +{ + "message": "success", + "batch_id": "3p$7rQ3p9sT5bzmF-gen-batch" +} +``` + +## Step 7: Monitoring and Analytics + +Track the performance and outcomes of your batch calls through the provided `batch_id` and campaign analytics. Adjust future batches based on the insights gained. + + + +```javascript SendCalls.js +// Headers +const headers = { + authorization: "YOUR-API-KEY-HERE", +}; + +// Data +const data = { + base_prompt: "You are calling {{business}} to renew their subscription to {{service}} before it expires on {{date}}.", + call_data: [ + { + phone_number: "1234567890", + business: "ABC co.", + service: "Netflix", + date: "September 4th", + }, + { + phone_number: "32176540987", + business: "XYZ inc.", + service: "Window Cleaning", + date: "December 20th", + }, + ], + label: "Renewal Reminder - Wednesday Afternoon with female voice", + voice_id: 0, + max_duration: 10, + reduce_latency: true, + wait_for_greeting: true, +}; + +// API request +await axios.post("https://api.bland.ai/v1/batches", data, { headers }); +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/send-first-call.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/send-first-call.mdx new file mode 100644 index 00000000000..a623083f6d4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/send-first-call.mdx @@ -0,0 +1,110 @@ +--- +title: Send your first phone call +slug: tutorials/send-first-call +--- + + + + +## Step 1: Setup Your Authorization + +Before making a call, you need to authenticate your request. Make sure you have your API key ready. + +Sign up on the [developer portal](https://app.bland.ai) to get yours. + +## Step 2: Prepare the Call Data + +You will need to provide specific details for the call. These include: + +- `phone_number`: The number you want to call. Remember to include the country code. +- `task`: Describe the purpose of the call and how the AI should handle the conversation. +- `voice_id`: Choose the voice persona (American male, Australian female, etc.) based on your preference. +- Set parameters like `reduce_latency`, `record`, `amd` (for navigating phone trees), and `wait_for_greeting` according to your call's requirements. + +## Step 3: Customize the Call + +You can further personalize the call by: + +1. Setting a `first_sentence` for the call. +2. Specifying `dynamic_data` to incorporate external API data. +3. Adjusting `voice_settings` for stability, similarity, and speed. +4. Choosing the language with the `language` parameter. +5. Setting a `max_duration` for the call. + +## Step 4: Send the API Request + +Use the provided JavaScript or Python code snippet to make the API request. + +## Step 5: Handle the Response + +After the call, you will receive a response with the `status` and `call_id`. If you set `record` to true, you can retrieve the recording using the `/call/recording` endpoint. + +Here's what an example response might look like: + +```javascript +{ + "status": "success", + "call_id": "9d404c1b-6a23-4426-953a-a52c392ff8f1" +} +``` + +## Step 6: Monitor the Call + +If you have set up a webhook, you will receive real-time notifications and transcripts once the call completes. + +And that's it! You're now ready to make your first AI-powered phone call with Bland AI. Happy calling! + + + +```javascript SendCall.js +// Headers +const headers = { + authorization: "YOUR-API-KEY-HERE", +}; + +// Data +const data = { + phone_number: "+11233456789", + task: "You are calling Fantastic Airlines on behalf of John Doe. Find out where John's Bags are located.", + voice_id: 0, + language: "eng", + request_data: { + calling: "Fantastic Airlines", + bag_claim: "69683", + airline_code: "UA123", + }, + record: true, + reduce_latency: true, + amd: true, +}; + +// API request +await axios.post("https://api.bland.ai/v1/calls", data, { headers }); +``` + +```python SendCall.py +# Headers +headers = { + 'authorization': 'YOUR-API-KEY-HERE' +} + +# Data +data = { + 'phone_number': '+11233456789', + 'task': "You are calling Fantastic Airlines on behalf of John Doe. Find the location of out where John's Bags are located.", + 'voice_id': 0, + 'request_data': { + 'calling': 'Fantastic Airlines', + 'bag_claim': '69683', + 'airline_code': 'UA123' + }, + 'record': True, + 'reduce_latency': True, + 'amd': True +} + +# API request +response = requests.post('https://api.bland.ai/v1/calls', json=data, headers=headers) +``` + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/sendfirstcall.jpg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/sendfirstcall.jpg new file mode 100644 index 00000000000..a509bcd6f04 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/sendfirstcall.jpg differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/transfer_node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/transfer_node.png new file mode 100644 index 00000000000..dc3cf41c12d Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/transfer_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook-fires-successfully.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook-fires-successfully.png new file mode 100644 index 00000000000..97ada0559b0 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook-fires-successfully.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook-signing.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook-signing.mdx new file mode 100644 index 00000000000..83176a4885d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook-signing.mdx @@ -0,0 +1,45 @@ +--- +title: Webhook Signing +slug: tutorials/webhook-signing +--- + + +Bland webhooks are signed with a secret key to ensure that they are not tampered with in transit and to confirm that they were sent by Bland. + +## Signing Webhooks + +When Bland sends a webhook, it calculates a signature using the HMAC algorithm with the SHA-256 hash function. The signature is then included in the `X-Webhook-Signature` header of the request. + +To create a webhook signing secret, first go to the [Account Settings in the Dev Portal](https://app.bland.ai/dashboard?page=settings) and click on the "Keys" tab. + +Here you can create a new secret by clicking "Replace Secret". It will only be shown once, so save it securely. + +## Verifying Webhooks + +To verify a webhook, you need to calculate the HMAC signature of the request body using the secret key and compare it to the signature in the `X-Webhook-Signature` header. + +Note that you must first create a webhook signing secret in the [Account Settings in the Dev Portal](https://app.bland.ai/dashboard?page=settings). + +Here is an example of how to verify a webhook in Node.js: + +```javascript +const crypto = require("node:crypto"); + +function verifyWebhookSignature(key, data, signature) { + const expectedSignature = crypto.createHmac("sha256", key).update(data).digest("hex"); + + return expectedSignature === signature; +} + +//... + +app.post("/webhook", (req, res) => { + const isValid = verifyWebhookSignature( + process.env.WEBHOOK_SECRET, + JSON.stringify(req.body), + req.headers["x-webhook-signature"], + ); + + //... +}); +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook.png new file mode 100644 index 00000000000..3791fac78a6 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook_node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook_node.png new file mode 100644 index 00000000000..dfeaa4cea5c Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhook_node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhooks.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhooks.mdx new file mode 100644 index 00000000000..b7d38677231 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/tutorials/webhooks.mdx @@ -0,0 +1,51 @@ +--- +title: Webhooks +slug: tutorials/webhooks +--- + + + + +## Introduction + +This is a quick tutorial to help you set up and test your Bland AI webhook. We'll include instructions both for inbound and outbound phone calls. + +We'll start with inbound because it's more popular. + +## Step 1: Create your webhook + +To create a test webhook visit [Webhook.site](https://webhook.site/) + +The website will automatically provide you a unique webhook URL. + +## Step 2: Connect to your inbound phone number + +Open your [developer portal](https://app.bland.ai) and visit the [inbound phone numbers](https://app.bland.ai/home?page=inbound-number) page. + + + +Paste your webhook into the `webhook` field. Make sure to remove the initial `https://` when you insert the URL. Then click `test webhook`. + +## Step 3: Verifying your outputs + +Navigate to webhook.site page, and check if the test webhook fired correctly. You'll know it worked because a new record will populate. + + + +At this point, if your record fails to populate, double check that you provided the correct URL - and that you REMOVED the initial `https://` from it. + +Otherwise, if issues persist, jump into the [discord](https://discord.gg/QvxDz8zcKe) - one of our teammates will help you asap. + +## Step 4: test a live phone call + +Call your inbound phone number. Once it ends, visit the Webhook site and confirm once again that a new record populated. + +If that's working, then you're set! + +## Step 5: Testing for outbound calls + +To test for outbound calls, once again create your webhook by referring back to step 1. + +Then, follow the [send phone call docs](https://docs.bland.ai/api-v1/post/calls) to create and send a phone call. Make sure you include the `webhook` as a parameter in your request. After, confirm that the webhook data populated on your webhook site page. + +And again, if you encounter issues, jump into [discord](https://discord.gg/QvxDz8zcKe) and message us - we will help asap. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/welcome-to-bland.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/welcome-to-bland.mdx new file mode 100644 index 00000000000..f59fb9f6a14 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/bland/fern/welcome-to-bland.mdx @@ -0,0 +1,53 @@ +--- +title: Welcome to Bland AI +slug: welcome-to-bland +--- + + +Bland is a platform for AI phone calling. Using our API, you can easily send or receive phone calls with a programmable voice agent. + +We really care about making our phone calls... + +1. **Fast**: sub-second latency from person speaking to AI responding. +2. **Reliable**: it's our responsibility, day in and day out, to make sure your phone calls work. No exceptions. +3. **Ultra flexible**: configure all aspects of your agent's behavior, by settings it's voice, creating transfer scenarios, configuring the initial greeting, etc. + +# What you can do with Bland + + + + Dispatch AI phone calls to call customers, leads, and to streamline operations. + + + Create inbound phone numbers for customer support, etc. + + + Connect external APIs and take live actions during phone calls. + + + Extract JSON data to answer questions about your calls. + + + Simultaneously send thousands of calls at once. + + + Fine-tune a custom LLM using your enterprise' call recordings and transcripts. + + + +# Getting started + + + + Read the API reference. + + + Learn to send your first phone call and test your agents. + + + Sign up on the developer portal. + + + Learn about enterprise features & meet with a member of the Bland AI team. + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/bank-transaction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/bank-transaction.mdx new file mode 100644 index 00000000000..b07e24324c6 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/bank-transaction.mdx @@ -0,0 +1,137 @@ +--- +title: Bank Transaction object +slug: api-reference/bank-transaction +--- + +Bank transactions are transactions that have occurred within a bank account owned by a business. + +### Attributes + + + Unique identifier for the bank transaction. + + + Unique ID of the bank transaction in your system for linking purposes. **Idempotency key.** + + + Resource type. Value will be "Bank_Transaction". + + + Id for the Business this transaction belongs to. + + + The source that the bank transaction was imported from. + Values can be: `UNIT`, `PLAID`, `API` + + + Unique ID of the bank transaction in its source system. **Idempotency key.** + + + Id of the source account in the source system. + + + Date the transaction occurred. + + + The direction of the transaction relative to the source account. + Values can be: `CREDIT`, `DEBIT` + + + The amount of the transaction in cents. + + + The name of the merchant or counterparty associated with the transaction. + + + Description of the transaction. + + + The type of bank account transaction. + Example values: `PURCHASE`, `BOOK`, `ATM`, `WIRE`, etc. + + + The status of the transaction's categorization in Layer's systems. + Values can be: `PENDING`, `READY_FOR_INPUT`, `CATEGORIZED`, `LAYER_REVIEW` + + + How the transaction was categorized. + Values can be: `SMS`, `API`, `LAYER_AUTO`, `LAYER_MANUAL` + + + The category assigned to the transaction. Only populated for transactions that have a finalized category. + + + String enum for the category assigned to the transaction. The set of category enums will vary based on chart of account configured for the business. + + + A human-readable string describing the category. This can be presented to the end user in your UI. + + + + + Layer's suggested categorization for the transaction. + + + The type of categorization approach used. + + + The category assigned to the transaction. Only populated for transactions that have a finalized category. + + + String enum for the category assigned to the transaction. The set of category enums will vary based on chart of account configured for the business. + + + A human-readable string describing the category. This can be presented to the end user in your UI. + + + + + Layer's list of suggested categories for the transaction. + + + String enum for the category assigned to the transaction. The set of category enums will vary based on chart of account configured for the business. + + + A human-readable string describing the category. This can be presented to the end user in your UI. + + + + + + + + + + +```json Example +{ + "id":"67cee0d8-3b8e-4b4b-a857-78ce3bb1d895", + "type":"Bank_Transaction", + "transaction_type":"Purchase", + "business_id":"cfee5365-dcc3-425e-b403-cc9568f7121e", + "source":"API", + "source_transaction_id":"11111113", + "source_account_id":"111113", + "imported_at":"2023-06-07T00:42:08.664543Z", + "date":"2023-05-15T14:13:07Z", + "direction":"Debit", + "amount":8026, + "counterparty_name":"SUNOCO", + "description":null, + "categorization_status":"CATEGORIZED", + "category":{ + "category":"FUEL", + "display_name":"Fuel" + }, + "categorization_method":"LAYER_AUTO", + "categorization_flow":{ + "type":"AUTO", + "category":{ + "category":"FUEL", + "display_name":"Fuel" + } + } +} +``` + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/business.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/business.mdx new file mode 100644 index 00000000000..809650c9f29 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/business.mdx @@ -0,0 +1,87 @@ +--- +title: Business object +slug: api-reference/business +--- + +A Business is Layer’s representation of an end business on your platform. The Business object contains all the information about the business needed for Layer’s embedded accounting logic. + +### Attributes + + + Unique identifier for the business. + + + Resource type. Value will be "Business". + + + Unique ID of the business in your system for linking purposes. **Idempotency key.** + + + Legal name of the business as it has been registered. + + + Tax identification number of the business. + + + Two letter state abbreviation. (`AK`, `AL`, `AR`, etc.) + + + Entity type of the business. Used to determine tax filing status. + Values can be: `SOLE_PROP`, `C_CORP`, `LLC`, `S_CORP`, `PARTNERSHIP` + + + Phone number used for SMS based categorization. + + + List of unit accounts associated with this business. + + + The Unit account's ID + + + + + Plaid items linked to this account. + + + `item_id` returned by Plaid on the initial link. + + + `access_token` returned by Plaid on the initial link. + + + + + Time when the business entity was created in Layer. **Eligible sort key.** + + + Time when the business' information was last updated in Layer. **Eligible sort key.** + + + + + +```json Example +{ + "id": "863ed926-e30d-40f4-8e7e-b0d5387ce4fb", + "type": "Business", + "external_id": "test-acme-id", + "legal_name": "ACME LLC", + "tin": null, + "business_activity_code": null, + "us_state": "CA", + "entityType": "LLC", + "phone_number": "+16504651359", + "imported_at": "2023-06-15T22:12:05.467940Z", + "updated_at": "2023-06-15T22:12:05.467940Z", + "archived_at": null, + "unit_accounts": [ + { + "id": "111111", + "imported_at": "2023-06-15T22:12:05.467940Z" + } + ] +} +``` + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/invoice.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/invoice.mdx new file mode 100644 index 00000000000..6ca1d18305d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/invoice.mdx @@ -0,0 +1,263 @@ +--- +title: Invoice object +slug: api-reference/invoice +--- + + +An Invoice represents an invoice that a Business has collected for goods and services provided. Invoices are used to pass information about sales and accounts receivable into Layer. + +### Attributes + + + Unique identifier for the invoice. + + + Resource type. Value will be "Invoice". + + + Unique ID of the invoice in your system for linking purposes. + + + Id of the Business that generated the invoice. + + + Status of the invoice. Values can be: `PENDING`, `SENT`, `PARTIALLY_PAID`, + `PAID`, `VOIDED` + + + When the invoice was sent by the business to the recipient. + + + When the invoice is due. + + + When the invoice was paid. + + + When the invoice was voided. Voiding excludes the invoice from accounting. + + + Number for the invoice for display to end-users. + + + String description of the invoice recipient. + + + Line items making up the invoice. + + + Id of the invoice line item. + + + Id of the parent invoice + + + Description of the specific line item. + + + Reference to the product being sold. + + + Number of units sold. + + + The amount in cents of each unit. + + + Total discount given to this line item, in cents. + + + + + + Ledger account associated with this tax. + + Type of tax account object. + + + Name of the tax account, if a name was specified when this line item was created. + + + Id of the tax account if either an account ID was used to create the tax line item or if no tax account was specified. + + + + + Amount, in cents, of tax owed. + + + + + Total discount given to this line item, in cents. + + + Indicates this line item is a prepayment for future services, e.g. package deals, gift cards, etc. + + + + + Subtotal of all invoice line items in cents. + + + Additional discount applied to the whole invoice in addition to individual + line items. + + + Sum of all discount amounts across the invoice line items and any additional + discounts in cents. + + + + + + Ledger account associated with this tax. + + Type of tax account object. + + + Name of the tax account, if a name was specified when this line item was created. + + + Id of the tax account if either an account ID was used to create the tax line item or if no tax account was specified. + + + + + Amount, in cents, of tax owed. + + + + + Sum of all taxes across the invoice line items and any additional taxes in + cents. + + + Tips included by the buyer, in cents. + + + Total amount of the invoice in cents. + + + The remaining balance on the invoice after factoring in all previous invoice + payments. + + + The payments that have been made towards the balance of the invoice. + + + Id of the invoice payment. + + + External id for the payment within your platform. **Idempotency key.** + + + Method used to make the payment. Values can be: `CASH`, `CHECK`, + `CREDIT_CARD`, `ACH`, `REDEEMED_PREPAYMENT`, `OTHER` + + + Fee associated with processing a payment, e.g. credit card processing + fees, in cents. + + + When the buyer payment occurred. + + + When the invoice was imported into Layer. + + + Processor used to make the payment, if any. + Any processor name can be provided and will be tracked. + Supported processors (`STRIPE`, `SHOPIFY`) will have additional asset balance tracking. + + + + + Id of the invoice being paid. + + + Id of the invoice payment. + + + Amount paid towards this invoice in cents. Cannot exceed the amount of + the associated payment. + + + + + + + Time when the invoice was first imported into Layer. **Eligible sort key.** + + + Time when the invoice was last updated in Layer. **Eligible sort key.** + + + +```json Response +{ + "data": { + "type": "Invoice", + "id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "business_id": "83d8fb80-31ee-4d57-b684-44b4aaa5e01f", + "external_id": "019234", + "status": "SENT", + "sent_at": "2024-04-02T09:02:00Z", + "due_at": "2023-04-02T09:02:00Z", + "paid_at": null, + "voided_at": null, + "invoice_number": "1", + "recipient_name": "John Doe", + "line_items": [ + { + "id": "e6a491dd-9c22-4403-a54f-32d741a7ec67", + "invoice_id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "account_identifier": null, + "description": null, + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": "2.00", + "subtotal": 2598, + "discount_amount": 0, + "sales_taxes_total": 218, + "sales_taxes": [ + { + "tax_account": { + "type": "Tax_Name", + "name": "CALIFORNIA_VAT" + }, + "amount": 218 + } + ], + "total_amount": 2816 + }, + { + "id": "44f06385-3ef5-4517-8095-eeedaf2054ab", + "invoice_id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "account_identifier": null, + "description": null, + "product": "Full drain cleaning service", + "unit_price": 25000, + "quantity": "1.00", + "subtotal": 25000, + "discount_amount": 0, + "sales_taxes_total": 0, + "total_amount": 25000 + } + ], + "subtotal": 27598, + "additional_discount": 250, + "additional_sales_taxes_total": 0, + "tips": 0, + "total_amount": 27566, + "outstanding_balance": 27566, + "payment_allocations": [], + "imported_at": "2024-04-19T02:23:59.902537Z", + "updated_at": null, + "transaction_tags": [] + } +} +``` + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/ledger.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/ledger.mdx new file mode 100644 index 00000000000..139f2498e3f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/ledger.mdx @@ -0,0 +1,239 @@ +--- +title: General Ledger objects +slug: api-reference/ledger +--- + +The general ledger of a business on Layer’s platform. A general ledger is automatically created when a [Business](/api-reference/business/business) is created via the [Create a Business](/api-reference/business/create) endpoint. + +### Attributes + + + Resource type. Value will be "Chart_Of_Accounts". + + + Array of [Ledger Accounts](/api-reference/ledger#ledger-account-object) within the Ledger. + + +#### Ledger Account object +An account within the general ledger + + Unique identifier of the ledger account. + + + Account name + + + Stable identifier for templated account. + + + Human readable description for this ledger account. + + + An array of [Ledger Account](/api-reference/ledger#ledger-account-object) objects representing child accounts of this ledger account. + + + Balance of the account, in cents. + + + Account balance normality. + Values can be: `DEBIT`, `CREDIT` + + + Array of [Ledger Account Line Item](/api-reference/ledger#ledger-account-line-item-object) objects representing ledger entries recorded in this account. + + +#### Ledger Account Line Item object +A ledger entry within a ledger account. + + Unique identifier of the ledger account line item. + + + Id of the Journal entry the ledger account line item was part of. + + + Simplified [Ledger Account](/api-reference/ledger#ledger-account-object) object containing this line item. + + + Direction of line item. + Values can be: `DEBIT`, `CREDIT` + + + Timestamp of the financial transaction associted with the line item. + + + Timestamp when ledger entry was added to the ledger account. + + +#### Journal Entry object +A journal entry within the general ledger. + + Unique identifier of the journal entry. + + + Id of the business associated with this journal entry. + + + Id of the general ledger containing this journal entry. + + + Entity that created the journal entry. + + + Type of entry. + Example values: `INVOICE_PAYMENT`, `EXPENSE` + + + Array of [Ledger Account Line Items](/api-reference/ledger#ledger-account-line-item-object) comprising the Journal Entry. + + + + +```json Example +{ + "data":{ + "type":"Chart_Of_Accounts", + "name":"Default", + "accounts":[ + { + "id":"86b497b9-71e3-4353-9726-8b4a5ac46626", + "number":0, + "pnlCategory":null, + "name":"Assets", + "accountStableName":"ASSETS", + "description":"All asset accounts for your business", + "subAccounts":[ + { + "id":"b0b4f2ef-9c46-4ee4-87f6-3db37cad4d5d", + "number":0, + "pnlCategory":null, + "name":"JP Morgan Chase Business Checking (4402)", + "accountStableName":"UNIT_CHECKING", + "description":"Unit checking", + "normality":"DEBIT", + "balance":478018, + "selfOnlyBalance":478018, + "entries":[ + { + "id":"63c52976-16a4-486a-86ab-00d2cc669e99", + "entry_id":"0e81ec02-483a-4ade-8878-b8e731e14c0f", + "account":{ + "id":"b0b4f2ef-9c46-4ee4-87f6-3db37cad4d5d", + "name":"JP Morgan Chase Business Checking (4402)", + "stable_name":"UNIT_CHECKING", + "normality":"DEBIT", + "pnl_category":null, + "always_show_in_pnl":false, + "description":"Unit checking" + }, + "amount":8531, + "direction":"CREDIT", + "entry_at":"2023-12-11T16:11:41.316Z", + "createdAt":"2023-12-11T16:11:43.875713Z" + }, + { + "id":"91c7b1b6-5ab5-4b11-b39d-1adf33841f11", + "entry_id":"e9394916-91d2-4ddb-8bfe-bbc25bb7d9da", + "account":{ + "id":"b0b4f2ef-9c46-4ee4-87f6-3db37cad4d5d", + "name":"JP Morgan Chase Business Checking (4402)", + "stable_name":"UNIT_CHECKING", + "normality":"DEBIT", + "pnl_category":null, + "always_show_in_pnl":false, + "description":"Unit checking" + }, + "amount":540981, + "direction":"DEBIT", + "entry_at":"2023-12-11T16:11:41.316Z", + "createdAt":"2023-12-11T16:11:43.949003Z" + } + ] + }, + { + "id":"bd68a8e3-fce4-4e7f-bee3-e7bd0f94627d", + "number":0, + "pnlCategory":null, + "name":"Accounts Receivable", + "accountStableName":"ACCOUNTS_RECEIVABLE", + "description":"Amounts owed by clients", + "normality":"DEBIT", + "balance":140485, + "selfOnlyBalance":140485, + "entries":[ + // Omitteed for length + ] + } + // Omitted for length + ], + "normality":"DEBIT", + "balance":618503, + "entries":[ + + ] + }, + { + "id":"d138a41a-0ea4-4949-a3c2-28279936fda1", + "number":0, + "pnlCategory":null, + "name":"Liabilities", + "accountStableName":"LIABILITIES", + "description":"All liabilities for your business", + "subAccounts":[ + // Omitted for brefity + ], + "normality":"CREDIT", + "entries":[ + + ] + }, + { + "id":"436a0187-1355-4e2d-978a-5ff4d52ad03f", + "number":0, + "pnlCategory":null, + "name":"Equity", + "accountStableName":"EQUITY", + "description":"All equity accounts for your business", + "subAccounts":[ + // Omitted for brefity + ], + "normality":"CREDIT", + "entries":[ + ] + }, + { + "id":"337ae99d-68a9-4ec2-b19d-4b8a5750f6b9", + "number":0, + "pnlCategory":"INCOME", + "name":"Revenue", + "accountStableName":"REVENUE", + "subAccounts":[ + // Omitted for brefity + ], + "normality":"CREDIT", + "balance":678466, + "entries":[ + + ] + }, + { + "id":"4f394bcb-e514-4ec9-bcd7-6612ebd146fe", + "number":0, + "pnlCategory":null, + "name":"Expenses", + "accountStableName":"EXPENSES", + "description":"Expenses", + "subAccounts":[ + // Omitted for brefity + ], + "normality":"DEBIT", + "balance":65963, + "entries":[ + + ] + } + ] + } +} +``` + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/payments.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/payments.mdx new file mode 100644 index 00000000000..1d29c765e11 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/payments.mdx @@ -0,0 +1,83 @@ +--- +title: Invoice Payment object +slug: api-reference/payments +--- + + +A refund represents a transaction that returns value to from a business to a customer. A specific payment can be refunded or a general refund can be applied to an invoice. + +### Attributes + + + Unique identifier for the payment. + + + Unique ID of the invoice payment in an external system for linking and + idempotency. + + + Timestamp when the payment was completed. + + + Payment method. Possible values are: `CASH`, `CHECK`, `CREDIT_CARD`, `ACH`, + `REDEEMED_PREPAYMENT`, `OTHER` + + + Fee paid by business for processing of payment in positive cents. + + + Customer payment amount, in cents. + + + Processor used to make the payment, if any. + Any processor name can be provided and will be tracked. + Supported processors (`STRIPE`, `SHOPIFY`) will have additional asset balance tracking. + + + Timestamp when the payment was imported into Layer. + + + Timestamp when the payment was imported into Layer. + + + Id of an invoice to which this payment is be applied. + + + Id of the payment this this allocationa applies from. + + + Customer payment amount, in cents. + + + + + +```json Response + { + "data": { + "type": "Payment", + "id": "e67c216b-28f4-4a0e-9a21-7f05c19e4c66", + "external_id": "payment-1", + "at": "2024-02-27T02:16:40.369432Z", + "method": "CREDIT_CARD", + "fee": 20, + "amount": 90, + "processor": "STRIPE", + "imported_at": "2024-02-27T02:16:40.389772Z", + "allocations": [ + { + "invoice_id": "57f0fada-bb56-4f3e-9afa-2a222b68009e", + "payment_id": "e67c216b-28f4-4a0e-9a21-7f05c19e4c66", + "amount": 90, + "transaction_tags": [] + } + ], + "transaction_tags": [] + } + } + ``` + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/plaid.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/plaid.mdx new file mode 100644 index 00000000000..9d9618af213 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/plaid.mdx @@ -0,0 +1,33 @@ +--- +title: Plaid Configuration object +slug: api-reference/plaid +--- + +Layer uses Plaid to connect to pull information from your customers' external bank accounts and credit cards. The Plaid Configuration object allows you to manage the Plaid configuration associated with your Layer account. + +### Attributes + + + Resource type. Value will be: "Plaid_Configuration" + + + Account-wide client id. + + + Account-wide plaid secret. Only the last 4 characters will be displayed. + + + + + +```json Example +{ + "data":{ + "type":"Plaid_Configuration", + "client_id":"6488fafd7a73ae00122004d6", + "secret_last_4":"acae" + } +} +``` + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/pnl.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/pnl.mdx new file mode 100644 index 00000000000..36483d4170c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/pnl.mdx @@ -0,0 +1,173 @@ +--- +title: Profit and Loss object +slug: api-reference/pnl +--- + +A Profit and Loss object represents a profit and loss report that has been generated for a [Business](/api-reference/business/business). + +Profit and Loss reports are generated for a specific time range and only contain information for categorized transactions that have been journaled to the general ledger. + +### Attributes + + + Id of the Business the profit and loss report was generated for. + + + Resource type. Value will be "Profit_And_Loss" + + + Start date for data included in the report. + + + End date for data in cluded in the report. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing the income line items in the report. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing the cost of goods sold line items in the report. + + + Gross profit in cents. Calculated by subtracting total cost_of_sales line items from total income line items. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing the expense line items in the report. + + + Net profit before taxes in cents. Calculated by subtracting total expenses line items from gross_profit. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing the tax line items in the report. + + + Net profit in cents. Calculated by subtracting total taxes line items from profit_before_taxes + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing business outflow transactions that are not part of the profit and loss statement. + + + A [Line Item](/api-reference/pnl/pnl#lineitem-object) object representing personal expense transactions that are not part of the profit and loss statement. + + + A boolean representing whether all imported transactions within the report date range have been categorized. If FALSE, there are uncategorized transactions within the selected date range, indicating the provided report data may be incomplete. + + +#### `Line Item` object + + Enum name for the line item. Ex. "REVENUE" + + + Display name for the line item. Ex. "Revenue" + + + The value of the line item in cents. + + + An array of [Line Item](/api-reference/pnl/pnl#lineitem-object) objects representing child line items within the report section. + + + +```json Profit and Loss +{ + "data":{ + "type":"Profit_And_Loss", + "business_id":"d2f6d97f-3345-4299-9ec2-468738c5d536", + "start_date":"2023-01-01T06:00:00Z", + "end_date":"2023-12-01T06:00:00Z", + "income":{ + "name":"REVENUE", + "display_name":"Revenue", + "value":49397, + "line_items":[ + { + "name":"SERVICES_REVENUE", + "display_name":"Service Revenue", + "value":46897, + "line_items":null + }, + { + "name":"GOODS_REVENUE", + "display_name":"Sale of Goods Revenue", + "value":0, + "line_items":null + }, + { + "name":"DISCOUNTS", + "display_name":"Discounts & Refunds", + "value":2500, + "line_items":null + } + ] + }, + "cost_of_goods_sold":{ + "name":"COGS", + "display_name":"Cost of Goods Sold", + "value":8026, + "line_items":[ + { + "name":"JOB_SUPPLIES", + "display_name":"Job supplies", + "value":8026, + "line_items":null + } + ] + }, + "gross_profit":41371, + "expenses":{ + "name":"OPERATING_EXPENSES", + "display_name":"Operating Expenses", + "value":0, + "line_items":[ + { + "name":"INSURANCE", + "display_name":"Insurance", + "value":0, + "line_items":null + }, + { + "name":"RENT_EXPENSE", + "display_name":"Rent", + "value":0, + "line_items":null + }, + { + "name":"UTILITIES", + "display_name":"Utilities", + "value":0, + "line_items":null + }, + { + "name":"EQUIPMENT", + "display_name":"Equipment & Tools", + "value":0, + "line_items":null + }, + { + "name":"ADVERTISING", + "display_name":"Advertising", + "value":0, + "line_items":null + }, + { + "name":"VEHICLE_EXPENSES", + "display_name":"Vehicle Expenses", + "value":0, + "line_items":null + } + ] + }, + "profit_before_taxes":41371, + "taxes":{ + "name":"TAXES", + "display_name":"Taxes", + "value":0, + "line_items":null + }, + "net_profit":41371, + "other_outflows":null, + "personal_expenses":null, + "fully_categorized":true + } +} +``` + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/refunds.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/refunds.mdx new file mode 100644 index 00000000000..3f8ef39911a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/api-reference/refunds.mdx @@ -0,0 +1,71 @@ +--- +title: Refunds object +slug: api-reference/refunds +--- + + +A refund represents a transaction that returns value to from a business to a customer. A specific payment can be refunded or a general refund can be applied to an invoice. + +### Attributes + + + Unique identifier for the refund. + + + Unique ID of the refund in your system for linking and idempotency. + + + Resource type. Value will be "Refund". + + + Timestamp when refund posted. + + + Amount of refund received by customer. + + + Fee, in cents, charged by payment processor for the refund. + + + Payment method. Possible values are: `CASH`, `CHECK`, `CREDIT_CARD`, `ACH`, `STORE_CREDIT`, + `OTHER` + + + Processor used to make the payment, if any. + Any processor name can be provided and will be tracked. + Supported processors (`STRIPE`, `SHOPIFY`) will have additional asset balance tracking. + + + Customer name of the recipient of the refund. + If invoice, line item, or payment ids are specified, they must match this recipient name. + + + Invoice ID this refund was applied to, if applicable. + This field does not need to be specified, but if `invoice_line_item_id` is also specified, it must belong to this invoice. + + + Invoice line item id specifying an exact product or service which was refunded. + + + Payment ID this refund was applied to, if applicable. + + + +```json Response + { + "data": { + "type": "Refund", + "id": "6195c7b0-acd6-4bcb-9417-57fabf5f772c", + "refunded_amount": 100, + "fee": 11, + "completed_at": "2024-03-25T01:46:59.309329Z", + "method": "STORE_CREDIT", + "processor": null, + "invoice_id": "905cbd23-db8a-4c9b-b5ea-05923eb88c23", + "invoice_line_item_id": "938cd7e1-0758-4965-96c1-724efd10825b", + "recipient_name": "John Doe" + }, + "meta": {} + } + ``` + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/authentication.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/authentication.mdx new file mode 100644 index 00000000000..b7583a5f1fd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/authentication.mdx @@ -0,0 +1,48 @@ +--- +title: Authentication +subtitle: Authenticating your calls to the Layer API +slug: authentication +--- + + +### Client credentials + +Layer uses OAuth2's client credentials flow to authenticate API clients. To start your development, we will give you a set of `client_id` and `client_secret` tokens. + + + To obtain a set of client credentials, reach out to our team [here](https://layerfi.com/#contact-form). + + + +### Getting a bearer token +Calls to the Layer API require a bearer access token. To receive an access token and make calls to other API endpoints, provide your `client_id` and `client_secret` in the body of a POST request to Layer’s authorization server as shown below. + +```bash +curl -X POST https://auth.layerfi.com/oauth2/token \ + -u : \ + -H "Content-Type: application/x-www-form-urlencoded" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "scope=https://sandbox.layerfi.com/sandbox" \ + --data-urlencode "client_id=" +``` + +The authorization server will respond with your granted access token. + +```json +{ + "access_token": "", + "expires_in": 3600, + "token_type": "Bearer" +} +``` + +### Making authenticated API calls + +Use the access token in requests to the API by including it as a Bearer token in the authorization header. + +```bash +curl https://sandbox.layerfi.com/whoami \ + -H "Authorization: Bearer " +``` + +Access tokens expire after 1 hour. To refresh your access token, make another call to Layer’s authorization endpoint with your `client_id` and `client_secret`. We recommend refreshing tokens for new sets of requests rather than persisting access tokens. \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/docs.yml b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/docs.yml new file mode 100644 index 00000000000..e87d6815a29 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/docs.yml @@ -0,0 +1,102 @@ +instances: [] +title: Layer +favicon: favicon.png +logo: + light: logo/layer_logo.svg + dark: logo/layer_logo.svg + href: http://layerfi.com + height: 28 +colors: + accentPrimary: + dark: '#0c48e5' + light: '#7e9be5' + background: + dark: '#090014' +tabs: + api-reference: + slug: api-reference + displayName: API Reference + documentation: + displayName: Documentation + slug: documentation +navigation: + - tab: api-reference + layout: + - section: Businesses + contents: + - page: Business object + path: api-reference/business.mdx + - section: Plaid Configuration + contents: [] + - section: Financial Activities + contents: + - section: Transactions + contents: + - page: Bank Transaction object + path: api-reference/bank-transaction.mdx + - section: Payouts + contents: [] + - section: Accounts Receivable + contents: + - section: Invoices + contents: + - page: Invoice object + path: api-reference/invoice.mdx + - section: Payments + contents: + - page: Invoice Payment object + path: api-reference/payments.mdx + - section: Refunds + contents: + - page: Refunds object + path: api-reference/refunds.mdx + - section: Reporting + contents: + - page: Profit and Loss object + path: api-reference/pnl.mdx + - section: General Ledger + contents: + - page: General Ledger objects + path: api-reference/ledger.mdx + - section: External Accounts + contents: + - section: Custom Accounts + contents: [] + - section: Tags + contents: [] + - section: Client Admin + contents: + - section: Configuration + contents: + - page: Plaid Configuration object + path: api-reference/plaid.mdx + - tab: documentation + layout: + - section: Get Started + contents: + - page: Overview + path: introduction.mdx + - page: Authentication + path: authentication.mdx + - page: Onboarding a Business + path: guides/business-onboarding.mdx + - section: Importing Financial Data + contents: + - page: Overview + path: guides/importing-data-overview.mdx + - page: Invoices and Payments + path: guides/importing-invoices.mdx + - page: Bank Accounts and Credit Cards + path: guides/importing-bank-transactions.mdx + - section: Offering Accounting + contents: + - page: Overview + path: guides/offering-accounting-overview.mdx + - page: Transaction Categorization + path: guides/transaction-categorization.mdx + - page: Accounting Reports + path: guides/reporting.mdx + - section: Embedded Components + contents: + - page: Overview + path: guides/embedded-components.mdx diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/favicon.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/favicon.png new file mode 100644 index 00000000000..8ad52ae1e71 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/favicon.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/fern.config.json b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/fern.config.json new file mode 100644 index 00000000000..2e3e1df85fd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/fern.config.json @@ -0,0 +1,4 @@ +{ + "version": "*", + "organization": "fern" +} \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/business-onboarding.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/business-onboarding.mdx new file mode 100644 index 00000000000..d3fbbe125f4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/business-onboarding.mdx @@ -0,0 +1,63 @@ +--- +title: Onboarding a Business +subtitle: Onboard one of your business customers to Layer +slug: guides/business-onboarding +--- + + +The first step when using Layer is to onboard your customer to Layer using the [Create a business](/api-reference/create-new-business) endpoint. + +This endpoint creates the record for your customer in Layer’s systems. There are many optional features, such as specifying external accounts connected via Plaid. The [Create a business](/api-reference/create-new-business) endpoint lists the full set of options. + +```bash Request +curl -X POST https://sandbox.layerfi.com/v1/businesses \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "external_id": "test-acme-id", + "legal_name": "ACME LLC", + "tin": null, + "us_state": "CA", + "entity_type": "LLC", + "phone_number": "+18005555555", + "unit_ids": [{"unit_id": "111111"}] + }' +``` + + +### Plaid credentials +If you've added Plaid credentials (see [Plaid Configuration](/api-reference/plaid)), link Plaid accounts by including Plaid item ids & access tokens in the `plaid_items` field. +```json +"plaid_items": [ + { "item_id": "item-id-1", "access_token": "access_token_1" }, + { "item_id": "item-id-3", "access_token": "access_token_3" } +] +``` + +The Layer API will respond with the created [Business](/api-reference/business) object. Within the object will be a unique id, which you will use to make calls on behalf of the business in subsequent steps. + +```json Response +{ + "data": { + "id": "863ed926-e30d-40f4-8e7e-b0d5387ce4fb", + "type": "Business", + "external_id": "test-acme-id", + "legal_name": "ACME LLC", + "tin": null, + "business_activity_code": null, + "us_state": "CA", + "entityType": "LLC", + "phone_number": "+16504651359", + "imported_at": "2023-06-15T22:12:05.467940Z", + "updated_at": "2023-06-15T22:12:05.467940Z", + "archived_at": null, + "unit_accounts": [ + { + "id": "111111", + "imported_at": "2023-06-15T22:12:05.467940Z" + } + ] + }, + "meta": {} +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/embedded-components.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/embedded-components.mdx new file mode 100644 index 00000000000..5fd195b943d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/embedded-components.mdx @@ -0,0 +1,215 @@ +--- +title: Overview +subtitle: Using Layer's prebuilt components to build bookkeeping directly into your app +slug: guides/embedded-components +--- + + +Layer publishes pre-built UI components to npm that can be dropped into any existing React app. These components handle all integration with Layer’s API with the exception of authentication, which must be done on your backend to avoid exposing credentials in your client-side apps. + +Currently available embedded components include: +* Bank Transaction Review and Categorization +* Profit & Loss Reports + * Month over month revenue & expense chart + * Monthly revenue & expense breakdowns + * Monthly expandable P&L table + +## React setup + + + + + The first step of using Layer's embedded components is to install the `layerfi/components` package via npm and your preferred package manager. + + ```bash + npm install @layerfi/components --save + ``` + + ```bash + yarn install @layerfi/components + ``` + + + Next, set up the `LayerProvider` context, which serves as the configuration for fetching data for an individual business. All individual UX components must be rendered within this context. + + **For local testing**, you can use your Layer staging credentials (`APP_ID` and `APP_SECRET`). + + ```tsx + import { LayerProvider } from "@layerfi/components"; + + + {...} + + ``` + + **In production**, you should use access tokens scoped to a specific business. + See the backend configuration section below for steps to retrieve scoped tokens: + + ```tsx + import { LayerProvider } from "@layerfi/components"; + + '} + > + {...} + + ``` + + + + **Option 1: Set primary colors in theme configuration** + + You can set a dark and light theme color for all components. + We recommend starting with simple customization like this using rgb, hsl or hex colors. + ```tsx + + {...} + + ``` + + **Option 2: Customize CSS variables** + + For more flexible customization, CSS variables are exposed to allow you to set colors and styles for the embedded components. + We recommend setting these variables within a scoped container to isolate the scope to Layer components. + In this example, we've set variables within the `.layer-container` class. + + + ```css + body .layer-container { + --color-black: #1a1a1a; + --color-white: white; + --color-neutral: #666666; + --color-neutral-50: #fafcfc; + --color-neutral-200: #eef0ef; + --color-neutral-700: #636665; + --color-red: #e46362; + --color-info-green: #29bc9b; + + --color-primary: var(--color-black); + --color-accent: var(--color-white); + --color-secondary: var(--color-neutral); + --color-success: var(--color-info-green); + --color-danger: var(--color-red); + --text-color-primary: var(--color-black); + --text-color-secondary: var(--color-neutral-700); + --bg-element-focus: var(--color-neutral-50); + + --font-family: "InterVariable", "Inter", sans-serif; + --font-family-numeric: "InterVariable", "Inter", sans-serif; + --text-sm: 12px; + --text-md: 14px; + --text-heading: 24px; + --font-weight-normal: 460; + --font-weight-bold: 580; + --spacing-sm: 12px; + --spacing-md: 16px; + --spacing-2xl: 36px; + --border-color: var(--color-neutral-200); + } + ``` + + + + Finally, add components to your pages as you would any React component. + + ```tsx + + ``` + + Some components have multiple sub components which can be optionally included for composition and layout customization. + + ```tsx + +
+ +
+ +
+ +
+ +
+ ``` + + Type definitions are available for all components to assist with discovering options & subcomponents. +
+ +
+ + + +## Backend setup + +To ensure an end user can only access their own data, the Layer component must be provided with a scoped access token which limits access to a specific business. This is a two step process. + + + + + Use your Layer-provided `client_id` and `client_secret` to obtain a platform-wide access token. See [Authentication](/authentication) for more information on this process. + This should be done server-side to avoid exposing your credentials in your client-side application. + + + + ```bash curl + curl -X POST https://auth.layerfi.com/oauth2/token \ + -u : \ + -H "Content-Type: application/x-www-form-urlencoded" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "scope=https://sandbox.layerfi.com/sandbox" \ + --data-urlencode "client_id=" + ``` + + ```tsx typescript + const clientId = ""; + const clientSecret = ""; + + const url = "https://auth.layerfi.com/oauth2/token"; + + axios.post(url, null, { + params: { + grant_type: "client_credentials", + scope: "https://sandbox.layerfi.com/sandbox", + client_id: clientId, + }, + auth: { + username: clientId, + password: clientSecret, + }, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); + ``` + + + + Finally, send this access token to the frontend and populate it in the `businessAccessToken` field of the `LayerProvider`context: + + ```tsx + + {...} + + ``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-bank-transactions.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-bank-transactions.mdx new file mode 100644 index 00000000000..8581e9523f4 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-bank-transactions.mdx @@ -0,0 +1,79 @@ +--- +title: Bank Accounts and Credit Cards +slug: guides/importing-bank-transactions +--- + + +Layer ingests data from your customers' business bank accounts and credit cards to incorporate into their accounting. Layer can connect to external customer accounts at traditional financial institutions as well as any embedded bank accounts and credit cards your platform already provides to customers. + +## External accounts +Layer leverages [Plaid](https://plaid.com/) to connect with your customers' accounts at external financial insitutions. + +### Using Layer's Plaid connection +If your platform does not already have a Plaid integration, you can utilize Layer's Plaid connection to import to your customers' external bank accounts and credit card data. Layer will manage all aspects of the Plaid integration on your behalf. + +To start importing customers' bank accounts and credit cards into Layer, embed Layer's _Bank Linking_ react component within your product. This component manages all aspects Layer's plaid connection and walks customers through the process of granting read-only access to their bank account and credit card activity. More detail on using Layer's embedded UI components is available [here](/guides/embedded-components). + +![Bank Linking Component](/images/bank-linking-component.png) + +Once your customers have connected their accounts, Layer will automatically start importing their bank account and credit card transactions. + +### Using your own Plaid connection +If your platform already has an integration with Plaid, you can grant Layer access to your existing Plaid data through Plaid's [processor token functionality](https://plaid.com/docs/api/processors/). Reach out to your Layer contact to set up this access. + +## Embedded accounts +If your platform provides an embedded bank account or credit card to your customers, you can pass this banking data to Layer as well. There are two ways to pass embedded banking activity to Layer: +* **API** - Pass bank and credit card transactions to Layer's API directly. +* **Layer integrations** - Layer has direct connections with top embedded banking platforms. You can grant Layer access to pull your customers' data from these platforms on your behalf. Layer currently supports: [Unit](https://www.unit.co/) and [Stripe Treasury](https://stripe.com/treasury). + +### Importing transactions via API +To import bank transactions to Layer, make a call to the [Import Bank Transactions](/api-reference/import-transactions) endpoint. This endpoint takes in the data on a bank transaction that is needed for Layer’s categorization and accounting logic. + +Note that the `bank_account_id` field should correspond to the `internal_bank_account_ids` field specified when [onboarding the Business](/guides/business-onboarding) as in the example below. + +```bash Request +curl -X POST https://sandbox.layerfi.com/v1/internal-bank-transactions \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '[{ + "external_id": 2093489, + "bank_account_id": "219384290", + "created_at": "2023-05-15T14:13:07Z", + "transaction_type": "Purchase", + "direction": "Debit", + "amount": 8026, + "balance": 40049, + "processed_counterparty_name": "HOME DEPOT", + "merchant_type": 5200, + "merchant_location": "HOUSTON, TX, US" + }]' +``` + +The API will respond with a list of ingested [Bank Transactions](/api-reference/bank-transaction). + +```json Response +{ + "data":[ + { + "type":"Flat_Bank_Transaction", + "id":"0e46b2b1-56b5-4ee4-bb5e-b0b708e50b47", + "bank_transaction_id":"2093489", + "created_at":"2023-05-15T14:13:07Z", + "transaction_type":"Purchase", + "bank_account_id":"219384290", + "business_id":"d2f6d97f-3345-4299-9ec2-468738c5d536", + "direction":"DEBIT", + "balance":40049, + "amount":8026, + "merchant_type":"5200", + "processed_counterparty_name":"HOME DEPOT", + "categorizationStatus":"PENDING" + } + ], + "meta":{ + } +} +``` + +### Using Layer's integrations +To leverage Layer's embedded banking integrations with [Unit](https://www.unit.co/) and [Stripe Treasury](https://stripe.com/treasury), reach out to your Layer conact. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-data-overview.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-data-overview.mdx new file mode 100644 index 00000000000..a05508039ed --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-data-overview.mdx @@ -0,0 +1,11 @@ +--- +title: Overview +slug: guides/importing-data-overview +--- + + +Once you have onboarded one of your customers to Layer by creating a [Business](/api-reference/business), you can start importing their financial data into Layer. This allows you to build the complete picture of financial activity to do their accounting. Layer can ingest the below types of data: +* [Invoices & Payments](/guides/importing-invoices) - The revenue collected by your business customers. This can be in-person sales made through a point of sale system, invoices sent through your software, ecommerce sales, or any other type of accounts receivable activity. +* [Bank Transactions](/guides/importing-bank-transactions) - Activity within your customers' business bank accounts and credit cards. +* **Payroll** - Payroll runs for paying employees and associated payroll taxes. Currently in Beta. Reach out to your Layer contact for access. +* **Bills** - Bills sent to your customers for their accounts payable obligations. Currently in Beta. Reach out to your Layer contact for access. \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-invoices.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-invoices.mdx new file mode 100644 index 00000000000..c385397a67d --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/importing-invoices.mdx @@ -0,0 +1,244 @@ +--- +title: Invoices and Payments +slug: guides/importing-invoices +--- + + +Invoices and Payments represent revenue collected by your business customers. This can be in-person sales made through a point of sale system, invoices sent through your software, ecommerce sales, or any other type of accounts receivable activity. + +To import invoices, make a call to the [Create Invoice](/api-reference/create-invoice-payments-from-batch) endpoint. This endpoint takes in the data on the sale your customer made, what was sold, any associated taxes, and how the sale was paid for. + +### Invoices without payments +Invoices can be created without any payments associated with them. Payments towards the invoice can then be recorded later on using the [Record an Invoice Payment](/api-reference/create-invoice-payments-from-batch) endpoint. + + +```bash Request +curl -X POST https://sandbox.layerfi.com/v1/businesses/:business_id/invoices \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "external_id": "019234", + "sent_at": "2024-04-02T09:02:00Z", + "due_at": "2023-04-02T09:02:00Z", + "invoice_number": "1", + "recipient_name": "John Doe", + "line_items": [ + { + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": 2, + "sales_taxes": [ + { + "tax_account": { + "type": "Tax_Name", + "name": "CALIFORNIA_VAT" + }, + "amount": 218 + } + ] + }, + { + "product": "Full drain cleaning service", + "unit_price": 25000, + "quantity": 1 + } + ], + "additional_discount": 250 + }' +``` + +The API will respond with an [Invoice](/api-reference/invoice) object. + +```json Response +{ + "data": { + "type": "Invoice", + "id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "business_id": "83d8fb80-31ee-4d57-b684-44b4aaa5e01f", + "external_id": "019234", + "status": "SENT", + "sent_at": "2024-04-02T09:02:00Z", + "due_at": "2023-04-02T09:02:00Z", + "paid_at": null, + "voided_at": null, + "invoice_number": "1", + "recipient_name": "John Doe", + "line_items": [ + { + "id": "e6a491dd-9c22-4403-a54f-32d741a7ec67", + "invoice_id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "account_identifier": null, + "description": null, + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": "2.00", + "subtotal": 2598, + "discount_amount": 0, + "sales_taxes_total": 218, + "sales_taxes": [ + { + "tax_account": { + "type": "Tax_Name", + "name": "CALIFORNIA_VAT" + }, + "amount": 218 + } + ], + "total_amount": 2816 + }, + { + "id": "44f06385-3ef5-4517-8095-eeedaf2054ab", + "invoice_id": "6d0c298f-3e4e-4538-9a71-1d5359c22f71", + "account_identifier": null, + "description": null, + "product": "Full drain cleaning service", + "unit_price": 25000, + "quantity": "1.00", + "subtotal": 25000, + "discount_amount": 0, + "sales_taxes_total": 0, + "total_amount": 25000 + } + ], + "subtotal": 27598, + "additional_discount": 250, + "additional_sales_taxes_total": 0, + "tips": 0, + "total_amount": 27566, + "outstanding_balance": 27566, + "payment_allocations": [], + "imported_at": "2024-04-19T02:23:59.902537Z", + "updated_at": null, + "transaction_tags": [] + } +} +``` + +### Invoices with payments +For instances where invoices are immediately paid, such as when using a point of sale device for in-person sales or online merchandise sales, you can also include payment information directly in the creation of the invoice. This looks very similar to the above example with the addition of payment information. + +```bash Request +curl -X POST https://sandbox.layerfi.com/v1/businesses/863ed926-e30d-40f4-8e7e-b0d5387ce4fb/invoices \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "external_id": "234988", + "sent_at": "2024-05-12T14:13:07Z", + "due_at": "2024-06-12T14:13:07Z", + "invoice_number": "2", + "recipient_name": "John Doe", + "line_items": [ + { + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": 1, + "sales_taxes": [ + { + "amount": 114 + } + ] + }, + { + "product": "Cleanout snake", + "unit_price": 18000, + "quantity": 1 + } + ], + "additional_sales_taxes": [ + { + "amount": 1291 + } + ], + "payments": [ + { + "external_id": "239872", + "method": "CREDIT_CARD", + "amount": 20704, + "processor": "MY_PROCESSOR" + } + ] + }' +``` + +In this case, the API will respond with the same created invoice, but with the invoice marked as fully or partially paid and the payment information included. + +```json Response +{ + "data": { + "type": "Invoice", + "id": "d01c9839-0378-4d44-b409-a0ae1e5dbb59", + "business_id": "83d8fb80-31ee-4d57-b684-44b4aaa5e01f", + "external_id": "234988", + "status": "PAID", + "sent_at": "2024-05-12T14:13:07Z", + "due_at": "2024-06-12T14:13:07Z", + "paid_at": "2024-04-19T02:24:00.009658Z", + "voided_at": null, + "invoice_number": "2", + "recipient_name": "John Doe", + "line_items": [ + { + "id": "fd60aa16-a0a6-40de-a814-b01836acfd36", + "invoice_id": "d01c9839-0378-4d44-b409-a0ae1e5dbb59", + "account_identifier": null, + "description": null, + "product": "Cleaner Solution Pro", + "unit_price": 1299, + "quantity": "1.00", + "subtotal": 1299, + "discount_amount": 0, + "sales_taxes_total": 114, + "sales_taxes": [ + { + "tax_account": { + "type": "Legder_Account_Id", + "id": "ba1a5e91-d04a-4c67-919e-f09a20d6e151" + }, + "amount": 114 + } + ], + "total_amount": 1413 + }, + { + "id": "4dd7708a-cad4-46e6-b5ff-34248a0b141e", + "invoice_id": "d01c9839-0378-4d44-b409-a0ae1e5dbb59", + "account_identifier": null, + "description": null, + "product": "Cleanout snake", + "unit_price": 18000, + "quantity": "1.00", + "subtotal": 18000, + "discount_amount": 0, + "sales_taxes_total": 0, + "total_amount": 18000 + } + ], + "subtotal": 19299, + "additional_discount": 0, + "additional_sales_taxes_total": 1291, + "additional_sales_taxes": [ + { + "tax_account": { + "type": "Legder_Account_Id", + "id": "ba1a5e91-d04a-4c67-919e-f09a20d6e151" + }, + "amount": 1291 + } + ], + "tips": 0, + "total_amount": 20704, + "outstanding_balance": 0, + "payment_allocations": [ + { + "invoice_id": "d01c9839-0378-4d44-b409-a0ae1e5dbb59", + "payment_id": "4d769e05-a101-4a16-8de5-6c37fbcec088", + "amount": 20704, + "transaction_tags": [] + } + ], + "imported_at": "2024-04-19T02:24:00.009658Z", + "updated_at": null, + "transaction_tags": [] + } +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/offering-accounting-overview.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/offering-accounting-overview.mdx new file mode 100644 index 00000000000..983de900c62 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/offering-accounting-overview.mdx @@ -0,0 +1,9 @@ +--- +title: Overview +slug: guides/offering-accounting-overview +--- + + +Once you have [imported](/guides/importing-data-overview) your customers' financial data into Layer, you can start offering them Layer's full suite of accounting features within your platform. This includes: +* [Transaction Categorization](/guides/transaction-categorization) - Categorize customer bank transactions into revenue/expense categories and automatically reconcile bank transactions with revenue, payroll, and bill data. This ensures customers' financial data is properly recorded in their general ledger. +* [Accounting Reports](/guides/reporting) - Surface real-time financial reports such as the profit and loss statement, balance sheet, or AR/AP aging summary. \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/reporting.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/reporting.mdx new file mode 100644 index 00000000000..2ad3050ffcf --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/reporting.mdx @@ -0,0 +1,147 @@ +--- +title: Accounting Reports +slug: guides/reporting +--- + + +Layer provides a full set of accounting charts and table reports that you can embed directly within your platform. You can use these reports to give your customers a clear picture of their business profitability, cash flows, and balance sheet. + +There are two ways to offer reporting within your platform: +* **Embedded UI components** - Embed Layer's charts and report UI components within your experience. +* **API** - Call Layer's API directly to build your own accounting reports. + + +## Embedded components +Layer provides a variety of chart and table report UI components that you can download via npm and embed directly within your frontend. Below are some examples: + +**Profit & Loss chart** +![Profit & Loss chart](/images/pnl-chart.png) + +**Profit & Loss table** +![Profit & Loss table](/images/pnl-table.png) + +More detail on using Layer's embedded UI components is available [here](/guides/embedded-components). Reach out to your Layer contact for a full list of embedded reporting components. + +## API + +### Retrieving a Profit & Loss report +To build your own Profit & Loss report UI make a call to the [retrieve a Profit and Loss Report](/api-reference/get-profit-and-loss-report-for-business) endpoint. + +The start_date query string parameter is required for all calls to the Retrieve a Profit and Loss Report endpoint. + + +```bash Request +curl -X GET https://sandbox.layerfi.com/v1/businesses/:business_id/reports/profit-and-loss?start_date=2023-01-01T00:00:00-06:00&end_date=2023-12-01T00:00:00-06:00 + -H "Authorization: Bearer " +``` + +The API will respond with the generated [Profit and Loss Report](/api-reference/pnl). + +```json Response +{ + "data":{ + "type":"Profit_And_Loss", + "business_id":"d2f6d97f-3345-4299-9ec2-468738c5d536", + "start_date":"2023-01-01T06:00:00Z", + "end_date":"2023-12-01T06:00:00Z", + "income":{ + "name":"REVENUE", + "display_name":"Revenue", + "value":49397, + "line_items":[ + { + "name":"SERVICES_REVENUE", + "display_name":"Service Revenue", + "value":46897, + "line_items":null + }, + { + "name":"GOODS_REVENUE", + "display_name":"Sale of Goods Revenue", + "value":0, + "line_items":null + }, + { + "name":"DISCOUNTS", + "display_name":"Discounts & Refunds", + "value":2500, + "line_items":null + } + ] + }, + "cost_of_goods_sold":{ + "name":"COGS", + "display_name":"Cost of Goods Sold", + "value":8026, + "line_items":[ + { + "name":"JOB_SUPPLIES", + "display_name":"Job supplies", + "value":8026, + "line_items":null + } + ] + }, + "gross_profit":41371, + "expenses":{ + "name":"OPERATING_EXPENSES", + "display_name":"Operating Expenses", + "value":0, + "line_items":[ + { + "name":"INSURANCE", + "display_name":"Insurance", + "value":0, + "line_items":null + }, + { + "name":"RENT_EXPENSE", + "display_name":"Rent", + "value":0, + "line_items":null + }, + { + "name":"UTILITIES", + "display_name":"Utilities", + "value":0, + "line_items":null + }, + { + "name":"EQUIPMENT", + "display_name":"Equipment & Tools", + "value":0, + "line_items":null + }, + { + "name":"ADVERTISING", + "display_name":"Advertising", + "value":0, + "line_items":null + }, + { + "name":"VEHICLE_EXPENSES", + "display_name":"Vehicle Expenses", + "value":0, + "line_items":null + } + ] + }, + "profit_before_taxes":41371, + "taxes":{ + "name":"TAXES", + "display_name":"Taxes", + "value":0, + "line_items":null + }, + "net_profit":41371, + "other_outflows":null, + "personal_expenses":null, + "fully_categorized":true + } +} +``` + +You can use this data to build your own profit and loss report UI within your platform. + +### Supported reports +A full list of supported reports is available in the [API reference](/api-reference/pnl) \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/transaction-categorization.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/transaction-categorization.mdx new file mode 100644 index 00000000000..8fbb376201a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/guides/transaction-categorization.mdx @@ -0,0 +1,175 @@ +--- +title: Transaction Categorization +slug: guides/transaction-categorization +--- + + +In order to get an accurate picture of their accounting, your customers must categorize and reconcile their bank account and credit card activity. This ensures all banking activity is recorded into the correct revenue and expense categories and matched with any other revenue, payroll, or bill data that has been imported into Layer. + +Transaction categorization is one of the most common workflows performed within accounting software and will usually be done by either your customer or a bookkeeper hired by your customer. + +Layer's embedded accounting platform allows your customers to complete their transaction categorization workflows directly within your platform. There are two ways to offer transaction categorization within your platform: +* **Embedded UI component** - Embed Layer's _Transaction List_ UI component within your experience. +* **API** - Build a customized transaction categorization workflows on top of Layer's transaction categorization API. + + +## Embedded component +Install Layer's Transaction List UI component via npm and embed it directly within your frontend. The Transaction List component handles all workflows related to transaction categorization including: displaying the list of transactions to categorize, surfacing suggested categorizations, matching transactions with invoices, and displaying the list of already categorized transactions. More detail on using Layer's embedded UI components is available [here](/guides/embedded-components). + +![Transaction List Component](/images/bank-transactions-component.png) + + +## API +For full control over the transaction categorization experience, you can build all transaction categorization workflows on top of Layer's API. + +### Retrieving categorized transactions +The first step of transaction categorization is to retrieve Layer’s assigned categories and recommended categorization flows for all bank account and credit card transactions that have been imported into Layer. This is done by making a call to the [List all Bank Transactions](/api-reference/bank-transactions/list) endpoint. + +```bash Request +curl https://sandbox.layerfi.com/v1/businesses/:business_id/bank-transactions \ + -H "Authorization: Bearer " +``` + +The API will respond with a list of all imported transactions. + +```json Response +{ + "data": [ + { + "type": "Bank_Transaction", + "id": "0e46b2b1-56b5-4ee4-bb5e-b0b708e50b47", + "business_id": "d2f6d97f-3345-4299-9ec2-468738c5d536", + "source": "UNIT", + "source_transaction_id": "2093489", + "source_account_id": "219384290", + "imported_at": "2023-12-15T05:45:06.150088Z", + "date": "2023-05-15T14:13:07Z", + "direction": "DEBIT", + "amount": 8026, + "counterparty_name": "HOME DEPOT", + "description": null, + "categorization_status": "READY_FOR_INPUT", + "category": null, + "categorization_method": null, + "categorization_flow": { + "type": "ASK_FROM_SUGGESTIONS", + "suggestions": [ + { + "type": "Account", + "id": "885a3a95-fe3c-40fb-8424-86c44b7def15", + "stable_name": "JOB_SUPPLIES", + "category": "JOB_SUPPLIES", + "display_name": "Job supplies" + }, + { + "type": "Account", + "id": "2b3e300a-0fe8-4581-bfa5-c5a74dae0a7f", + "stable_name": "EQUIPMENT", + "category": "EQUIPMENT", + "display_name": "Equipment & Tools" + }, + { + "type": "Account", + "id": "3015ef8d-42aa-485e-90a9-3263f55155bc", + "stable_name": "VEHICLE_EXPENSES", + "category": "VEHICLE_EXPENSES", + "display_name": "Vehicle Expenses" + } + ] + }, + "projected_income_category": "EXPENSE" + } + ], + "meta": { + "pagination": { + "sort_by": "date", + "sort_order": "ASC", + "cursor": null, + "has_more": false + } + } +} +``` + +Each Bank Transaction in the returned list will include two fields: +- `category` is the journaled category that has been assigned to the transaction and determines how the transaction will be journaled to the general ledger of the business in Layer’s systems. When Layer has high confidence in the category of a transaction, this will be set automatically. It can also be populated using input from the end user on your platform. +- `categorization_flow` is Layer's recommended approach for categorizing the transaction. In some cases, this will be automatically categorizing the transaction without any human input. In other cases, Layer will recommend prompting the end user to select from a list of suggested categories. + +### Categorizing transactions +Transactions can be categorized via API any time after they’ve been imported, regardless of whether the business has SMS based categorized flows turned on or off. + +Transactions are categorized one at a time with a `PUT` request to the [Categorize Bank Transaction](/api-reference/bank-transaction/categorize) endpoint. Transactions can be recategorized after an original categorization is assigned, however this will change reports for any time period including the transaction. + +```bash Request +curl -X PUT https://sandbox.layerfi.com/v1/businesses/:business_id/bank-transactions/:transaction_id/categorize \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "type": "Category", + "category": { + "type": "StableName", + "stable_name": "JOB_SUPPLIES" + } +}' +``` + +The API will respond with a single [Bank Transaction](/api-reference/bank-transaction/bank-transaction) object. + +```json Response +{ + "data":{ + "type":"Bank_Transaction", + "id":"0e46b2b1-56b5-4ee4-bb5e-b0b708e50b47", + "business_id":"d2f6d97f-3345-4299-9ec2-468738c5d536", + "source":"UNIT", + "source_transaction_id":"2093489", + "source_account_id":"219384290", + "imported_at":"2023-12-15T05:45:06.150088Z", + "date":"2023-05-15T14:13:07Z", + "direction":"DEBIT", + "amount":8026, + "counterparty_name":"HOME DEPOT", + "description":null, + "categorization_status":"CATEGORIZED", + "category":{ + "type":"Account", + "id":"885a3a95-fe3c-40fb-8424-86c44b7def15", + "stable_name":"JOB_SUPPLIES", + "category":"JOB_SUPPLIES", + "display_name":"Job supplies" + }, + "categorization_method":"API", + "categorization_flow":{ + "type":"ASK_FROM_SUGGESTIONS", + "suggestions":[ + { + "type":"Account", + "id":"885a3a95-fe3c-40fb-8424-86c44b7def15", + "stable_name":"JOB_SUPPLIES", + "category":"JOB_SUPPLIES", + "display_name":"Job supplies" + }, + { + "type":"Account", + "id":"2b3e300a-0fe8-4581-bfa5-c5a74dae0a7f", + "stable_name":"EQUIPMENT", + "category":"EQUIPMENT", + "display_name":"Equipment & Tools" + }, + { + "type":"Account", + "id":"3015ef8d-42aa-485e-90a9-3263f55155bc", + "stable_name":"VEHICLE_EXPENSES", + "category":"VEHICLE_EXPENSES", + "display_name":"Vehicle Expenses" + } + ] + }, + "projected_income_category":"EXPENSE" + } +} +``` + +The `categorization_method` field will be populated to identify how the category was set (ex. “API”) + +Even after categorization, the `categorization_flow` and nested `suggestions` array are retained on the [Bank Transaction](/api-reference/bank-transaction/bank-transaction) object so that they can be re-displayed in a UI if a user wants to recategorize. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/introduction.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/introduction.mdx new file mode 100644 index 00000000000..a920268e932 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/introduction.mdx @@ -0,0 +1,23 @@ +--- +title: Overview +slug: introduction +--- + + +The Layer API provides access to Layer's embeddable accounting software. Using the API, you can seamlessly pass your customers' financial data into Layer's accounting software and surface a complete SMB accounting product within your platform. + +## API Environments + +The Layer API has two environments: +- **Sandbox:** `sandbox.layerfi.com` - Test environment for building your integration with Layer. +- **Production:** `api.layerfi.com` - Production environment for live usage with your SMB customers. + +## Getting an API key + +The first step to getting started is to contact us to obtain an API key. We will first send you a sandbox API key to use during development. Once you feel ready to go live and we review your integration, we'll provide your production API key. + +To obtain your API key and get started, reach out to our team [here](https://layerfi.com/#contact-form). + +## Getting started + +If you're just getting started, read about authenticating to our API and then check out our guides. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/logo/layer_logo.svg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/logo/layer_logo.svg new file mode 100644 index 00000000000..3081990d535 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/layerfi/fern/logo/layer_logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/memories.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/memories.mdx new file mode 100644 index 00000000000..95d8bbe3ef8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/memories.mdx @@ -0,0 +1,197 @@ +--- +title: Memories +subtitle: >- + Memories are the central data structure in Zep's Memory Store. They contain a + list of Messages and a Summary. +slug: chat-history-memory/memories +--- + + + + A Memory is the central data structure in Zep's Memory Store. It contains a list of Messages and a Summary (if + created). + + The Memory and Summary are returned with UUIDs, token counts, timestamps, and other metadata. + Memories are associated with [Sessions](sessions) in a many-to-one relationship. + + +## Persisting a Memory to a Session + +A Memory may include a single message or a series of messages. Each Message has a `role`, `role_type` and `content` field, with +role being the identifiers for your human and AI/agent users, and content being the text of the message. + +Additionally, you can store custom metadata with each Message. + + +**Sessions don't need to be explicitly created** + +Sessions are created automatically when adding Memories. If the `SessionID` is already exists, then the Memory is +upserted into the Session. + +[Manually creating a session](sessions) can be useful if you want to associate it with a user or add metadata. + + + + + + +```python +from zep_python import ( + NotFoundError, + ZepClient, +) +from zep_python.memory import Memory +from zep_python.message import Message +import uuid + +API_KEY = "" +client = ZepClient(api_key=API_KEY) + +session_id = uuid.uuid4().hex # A new session identifier + +history = [ + {"role": "Jane", "role_type": "user", "content": "Who was Octavia Butler?"}, + {"role": "HistoryBot", "role_type": "assistant", "content": "Octavia Estelle Butler (June 22, 1947 – February 24, 2006) was an American science fiction author."}, + {"role": "Jane", "role_type": "user", "content": "Which books of hers were made into movies?", "metadata": {"foo": "bar"}}, +] + +messages = [Message(role=m["role"], content=m["content"], metadata=m.get("metadata")) for m in history] +memory = Memory(messages=messages) +result = client.memory.add_memory(session_id, memory) +``` + + + + +```typescript +import { ZepClient, Message, Memory } from "@getzep/zep-js"; +import * as uuid from "uuid"; + +const API_KEY = ""; +const zepClient = await ZepClient.init(process.env.ZEP_API_KEY); + +const session_id: string = uuid.v4(); // A new session identifier + +const history: { role: string; content: string; metadata?: { [key: string]: any } }[] = [ + { role: "Jane", role_type: "user", content: "Who was Octavia Butler?" }, + { + role: "HistoryBot", + role_type: "assistant", + content: "Octavia Estelle Butler (June 22, 1947 – February 24, 2006) was an American science fiction author.", + }, + { role: "Jane", role_type: "user", content: "Which books of hers were made into movies?", metadata: { foo: "bar" } }, +]; + +const messages: Message[] = history.map((m) => new Message({ role: m.role, content: m.content, metadata: m.metadata })); +const memory: Memory = new Memory({ messages }); +const result = client.memory.addMemory(session_id, memory); +``` + + + + +## Getting a Session's Memory + +Read more about the difference between [Perpetual and Message Window Buffer Memory](/chat-history-memory). + +### Perpetual Memory + +The example below uses Zep's async API and a context manager. You may also use the sync API's `client.memory.get_memory` +method and use the `client` outside of a context manager. + + + + +```python +async with ZepClient(api_key=api_key) as client: + try: + memory = await client.memory.aget_memory(session_id, memory_type="perpetual") + for message in memory.messages: + print(message.to_dict()) + except NotFoundError: + print("Memory not found") +``` + + + + +```typescript +const client = await ZepClient.init(api_key); +try { + const memory = await client.memory.getMemory(session_id, "perpetual"); + for (const message of memory.messages) { + console.log(message.toDict()); + } +} catch (error) { + if (error instanceof NotFoundError) { + console.log("Memory not found"); + } else { + throw error; + } +} +``` + + + + +```json title="Output:" +[ + { + "uuid": "7291333f-2e01-4b06-9fe0-3efc59b3399c", + "created_at": "2023-05-16T21:59:11.057919Z", + "role": "HistoryBot", + "role_type": "assistant", + "content": "Parable of the Sower is a science fiction novel by Octavia Butler, published in 1993. It follows the story of Lauren Olamina, a young woman living in a dystopian future where society has collapsed due to environmental disasters, poverty, and violence.", + "token_count": 56 + }, + { + "uuid": "61f862c5-945b-49b1-b87c-f9338518b7cb", + "created_at": "2023-05-16T21:59:11.057919Z", + "role": "Jane", + "role_type": "user", + "content": "Write a short synopsis of Butler's book, Parable of the Sower. What is it about?", + "token_count": 23 + } +] +``` + +### Message Window Buffer Memory + + + + + +```python +async with ZepClient(api_key) as client: + try: + memory = await client.memory.aget_memory(session_id) + for message in memory.messages: + print(message.to_dict()) + except NotFoundError: + print("Memory not found") +``` + + + + +```typescript +const client = await ZepClient.init(api_key); +try { + const memory = await client.memory.getMemory(session_id, "message_window"); + for (const message of memory.messages) { + console.log(message.toDict()); + } +} catch (error) { + if (error instanceof NotFoundError) { + console.log("Memory not found"); + } else { + throw error; + } +} +``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/messages.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/messages.mdx new file mode 100644 index 00000000000..c639dd0b9bf --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/messages.mdx @@ -0,0 +1,198 @@ +--- +slug: chat-history-memory/messages +--- + + + Messages are the individual components of a conversation, each linked to a [Session](sessions) in a many-to-one + relationship. To incorporate messages into sessions, utilize the [Memories](memories) feature. + + +## Get a Specific Message from a Session + +To fetch a particular message from a session, utilize the `get_session_message` method. Below are examples in Python and TypeScript: + + + + + +```python +async with ZepClient(api_key=api_key) as client: + try: + session_id = "3e0e4af9-71ff-4541-b206-6133574bbbc6" # Replace with the actual session_id + message_id = "3e0e4af9-71ff-4541-b206-6133574bbbc7" # Replace with the actual message_id + message = await client.message.aget_session_message(session_id, message_id) + print(message.to_dict()) + except NotFoundError: + print("Message not found") +``` + + + + +```typescript +const client: ZepClient = await ZepClient.init(api_key); + +try { + const session_id: string = "3e0e4af9-71ff-4541-b206-6133574bbbc6"; // Replace with the actual session_id + const message_id: string = "3e0e4af9-71ff-4541-b206-6133574bbbc7"; // Replace with the actual message_id + + const message: Message = await client.message.getSessionMessage(session_id, message_id); + console.log(message); +} catch (error) { + if (error instanceof NotFoundError) { + console.log("Message not found"); + } else { + throw error; + } +} +``` + + + + +```json +{ + "uuid": "3e0e4af9-71ff-4541-b206-6133574bbbc7", + "created_at": "2023-12-08T22:17:33.185756Z", + "updated_at": "0001-01-01T00:00:00Z", + "role": "human", + "role_type": "user", + "content": "Who were her contemporaries?", + "metadata": { + "system": { + "entities": [], + "intent": "The subject is requesting information about the people who were living at the same time as the woman in question." + } + } +} +``` + +## Getting all Messages from a Session + + + + + +```python +async with ZepClient(api_key) as client: + try: + messages = await client.message.aget_session_messages(session_id) + for message in messages: + print(message.to_dict()) + except NotFoundError: + print("Sesssion not found") +``` + + + + +```typescript +const client: ZepClient = await ZepClient.init(api_key); + +try { + const messages: Message[] = await client.message.getSessionMessages(session_id); + messages.forEach((message: Message) => { + console.log(message); + }); +} catch (error) { + if (error instanceof NotFoundError) { + console.log("Message not found"); + } else { + throw error; + } +} +``` + + + + +```json +{ + "messages": [ + { + "uuid": "3e0e4af9-71ff-4541-b206-6133574bbbc7", + "created_at": "2023-12-08T22:17:33.185756Z", + "updated_at": "0001-01-01T00:00:00Z", + "role": "human", + "role_type": "user", + "content": "Who were her contemporaries?", + "metadata": { + "system": { + "entities": [], + "intent": "The subject is requesting information about the people who were living at the same time as the woman in question." + } + }, + "token_count": 0 + } + ], + ... +} +``` + +## Updating Session Message Metadata + +You have the ability to modify the metadata of a message. Please provide the metadata in the following format. Note that at this time, it's not possible to update the content of a message itself. + +```json +{ + "metadata": { + "foo": "bar" + } +} +``` + + + + + +```python +async with ZepClient(api_key) as client: + try: + session_id = "3e0e4af9-71ff-4541-b206-6133574bbbc6" # Replace with the actual session_id + message_uuid = "3e0e4af9-71ff-4541-b206-6133574bbbc7" # Replace with the actual message_id + metadata = { + "metadata": { + "foo": "bar" + } + } + await client.message.update_session_message_metadata(session_id, message_id, metadata) + except NotFoundError: + print("Session not found") +``` + + + + +```typescript +const client: ZepClient = await ZepClient.init(api_key); + +try { + const session_id: string = "3e0e4af9-71ff-4541-b206-6133574bbbc6"; // Replace with the actual session_id + const message_uuid: string = "3e0e4af9-71ff-4541-b206-6133574bbbc7"; // Replace with the actual message_id + const metadata: any = { metadata: { foo: "bar" } }; // Replace with the desired metadata + await client.message.updateSessionMessageMetadata(session_id, message_uuid, metadata); +} catch (error) { + console.error("Session not found", error); +} +``` + + + + +```json +{ + "uuid": "3e0e4af9-71ff-4541-b206-6133574bbbc7", + "created_at": "2023-12-08T22:17:33.185756Z", + "updated_at": "0001-01-01T00:00:00Z", + "role": "human", + "role_type": "user", + "content": "Who were her contemporaries?", + "metadata": { + "foo": "bar", + "system": { + "entities": [], + "intent": "The subject is requesting information about the people who were living at the same time as the woman in question." + } + } +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/overview.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/overview.mdx new file mode 100644 index 00000000000..fef8d81aa8f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/overview.mdx @@ -0,0 +1,78 @@ +--- +title: Overview +subtitle: >- + With Zep's Chat History features you can ensure that your your assistant's + responses are always relevant to the conversation at hand. +slug: chat-history-memory/overview +--- + + +Zep's Memory API persists your users' chat history and metadata to a [Session](sessions), enriches the memory, and +enables vector similarity search over historical chat messages and dialog summaries. + +Zep offers several approaches to populating prompts with context from historical conversations. + +## Perpetual Memory + +Perpetual Memory is the default memory type. Salient facts from the dialog are extracted and stored in a `Fact Table`. This +is updated in real-time as new messages are added to the Session. + +Every time you call the Memory API to get a Memory, +Zep returns the Fact Table, the most recent messages (per your `Message Window` setting), and a summary of the most recent +messages prior to the `Message Window`. + +We've found that including the combination of the Fact Table, summary, and the most recent messages in a prompts provides both factual +context and nuance to the LLM. + + + Perpetual Memory + + +## Summary Retriever Memory + +The Memory API returns the most recent messages and a summary of past messages relevant to the +current conversation, enabling you to provide your Assistant with helpful context from past conversations. In essence, +Perpetual memory +is a RAG pipeline over the entire conversation history and that executes in hundreds +of milliseconds. + +You don't need to do anything to enable this feature, beyond adding messages to a Session and +calling the `get_memory` API and requesting the `perpetual` `memory_type`. + + + Want to learn more about how Summary Retriever works? [Read How Perpetual Memory + Works](#how-summary-retriever-memory-works). + + +## Message Window Buffer Memory + +The Memory API returns the most recent `N` messages from the current conversation, where `N` is a +configurable `Message Window` set in your [Project Settings](/getting-started/projects). + +A summary of a messages older than the `N`th most recent message is also returned, if available. + +## How Summary Retriever Memory Works + +Perpetual Memory extracts the most relevant historical dialog from a Session and, with very low latency, returns to via +the Zep Memory API. + +When new Messages are added to a Session, Zep generates a new summary from a trailing series of messages in the dialog. +This is ongoing, ensuring that the entire conversation is incrementally summarized. +These summaries are also embedded, enabling vector similarity search over the entire series of conversation summaries. + + + Summary Retriever Flow Chart + + +Every time you call the Memory API to get a Memory, Zep uses a proprietary, low-latency language model to generate a +standalone +question from the most recent messages in the Session. This question is then used to search over the entire series of +conversation summaries. + +The most relevant summaries are returned and re-ranked in order to +improve their relevance and diversity. The top summaries are then themselves summarized using a +low-latency summarization model. The resulting summary and most recent messages are then returned via the API for use in +populating your prompt. + +The entire process takes hundreds of milliseconds, so that you're able to rapidly populate your prompt with relevant +context. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/search.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/search.mdx new file mode 100644 index 00000000000..5f02afdef57 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/search.mdx @@ -0,0 +1,196 @@ +--- +title: Vector Search Over Chat History +subtitle: >- + Zep helps developers search through long-term memory stores to find relevant + historical conversations efficiently. With automated embedding and advanced + indexing, Zep offers robust search capabilities that are straightforward and + effective. +slug: chat-history-memory/search +--- + + +## Searching for Messages or Summaries + +Zep enables vector similarity searches for Messages or Summaries stored within its system. This feature lets you populate prompts with past conversations that are contextually similar to a specific query, organizing the results by a similarity Score. + +### Choosing Between Summaries and Messages + +Zep supports searches for both Messages and Summaries. Since individual messages might miss some conversational context, Summaries are often the preferred choice for executing searches. For more on this, check out the section on [message limitations](#limitations-when-searching-over-messages-or-short-document-chunks). + +### MMR Reranking for Summaries + +Summaries can sometimes overlap in information, especially when the Message Window is set low. In such cases, employing [Maximum Marginal Relevance (MMR)](/working-with-search#how-zeps-mmr-re-ranking-works) to rerank search results can be beneficial. Zep includes built-in, hardware-accelerated support for MMR, making it simple and easy to use. + + +**Constructing Search Queries** + +Zep's Collection and Memory search support semantic search queries, JSONPath-based metadata filters, and a combination +of both. + +Memory search also supports querying by message creation date. + +Read more about [constructing search queries](/working-with-search#maximal-marginal-relevance-re-ranking). + + + + + + + +```python +from zep_python import ( + MemorySearchPayload, + ZepClient, +) + +# This uniquely identifies the user's session +session_id = "my_session_id" + +# Initialize the Zep client before running this code +search_payload = MemorySearchPayload( + text="Is Lauren Olamina a character in a book?", + search_scope="summary", # This could be messages or summary + search_type="mmr", # remove this if you'd prefer not to rerank results + mmr_lambda=0.5, # tune diversity vs relevance +) + +search_results = await client.memory.asearch_memory(session_id, search_payload) + +for search_result in search_results: + # Uncomment for message search + # print(search_result.messsage.dict()) + print(search_result.summary.dict()) +``` + + + + +```typescript +import { MemorySearchPayload, ZepClient } from "@getzep/zep-js"; + +// This uniquely identifies the user's session +const session_id: string = "my_session_id"; + +// Initialize the Zep client before running this code +const search_payload: MemorySearchPayload = { + text: "Is Lauren Olamina a character in a book?", + search_scope: "summary", // This could be messages or summary + search_type: "mmr", // Remove this if you'd prefer not to rerank results + mmr_lambda: 0.5, // Tune diversity vs relevance +}; + +const search_results = await client.memory.searchMemory(session_id, search_payload); + +search_results.forEach((search_result) => { + // Uncomment for message search + // console.log(search_result.message); + console.log(search_result.summary); +}); +``` + + + + +```json +{ + "summary": { + "uuid": "b47b83da-16ae-49c8-bacb-f7d049f9df99", + "created_at": "2023-11-02T18:22:10.103867Z", + "content": "The human asks the AI to explain the book Parable of the Sower by Octavia Butler. The AI responds by explaining that Parable of the Sower is a science fiction novel by Octavia Butler. The book follows the story of Lauren Olamina, a young woman living in a dystopian future where society has collapsed due to environmental disasters, poverty, and violence.", + "token_count": 66 + }, + "metadata": null, + "dist": 0.8440576791763306 +} +``` + +## Hybrid Search for Chat History with Metadata Filters + +Besides the vector similarity search for Messages and Summaries stored in Zep, you can also use metadata filters for your searches. You also have the option to conduct searches based purely on metadata. + + + + + +```python +zep_client.memory.search_memory( + session_id=session_id, + search_payload=MemorySearchPayload( + query="I enjoy reading science fiction.", + metadata={ + "where": {"jsonpath": '$[*] ? (@.foo == "bar")'}, + }, + ), +) +``` + + + + +```typescript +await client.memory.searchMemory(session_id, { + query: "I enjoy reading science fiction.", + metadata: { + where: { jsonpath: '$[*] ? (@.foo == "bar")' }, + }, +}); +``` + + + + +```json +{ + "dist": 0.7170433826192629, + "message": { + "content": "I've read many books written by Octavia Butler.", + "created_at": "2023-06-03T22:00:43.034056Z", + "metadata": { + "foo": "bar", + "system": { + "entities": [ + { + "Label": "PERSON", + "Matches": [ + { + "End": 46, + "Start": 32, + "Text": "Octavia Butler" + } + ], + "Name": "Octavia Butler" + } + ] + } + }, + "role": "human", + "token_count": 13, + "uuid": "8f3a06dd-0625-41da-a2af-b549f2056b3f" + }, + "metadata": null, + "summary": null +} +``` + +### Search Ranking and Limits + +#### Vector Indexes + +Zep automatically creates HNSW (Hierarchical Navigable Small World) indexes for all messages, summaries, and documents. This means you get speedy and relevant search results right out of the box, without the hassle of manually setting up or integrating a vector store and building indexes. Zep uses an optimized distance function similar to cosine distance for search ranking. + +#### Embedding Models + +Zep uses the `BAAI/bge-large-en` model for text embeddings, known for its high performance and optimization for semantic search. Keep in mind, this model has a 512 token maximum sequence length, which is important when deciding how to chunk your documents. + +#### Limitations When Searching Over Messages or Short Document Chunks + +Zep can return all messages from a search up to a certain row limit. This limit can be adjusted by passing a `limit` query string argument to the search API. Due to the sparsity issue we'll touch on below, we recommend sticking to the top 2-3 messages in your prompts. Or, you could analyze your search results and use a distance threshold to filter out messages that aren't relevant. + + +**Handling Short Texts in Embeddings** + +Searching through chat histories can be tricky. Chat messages are often brief and might not carry much "information". When these short texts are turned into high-dimensional embedding vectors, the result can be very sparse vectors. + +This sparsity means a lot of these vectors end up being close to each other in the vector space, which can lead to a higher chance of getting false positives in your search results for relevant messages. As a result, we recommend searching over Summaries, which include more information than Messages. + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/sessions.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/sessions.mdx new file mode 100644 index 00000000000..1c61b9b134c --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/sessions.mdx @@ -0,0 +1,180 @@ +--- +slug: chat-history-memory/sessions +--- + + +Sessions represent a conversation. Sessions can be associated with [Users](/users) in a 1:M relationship. + +Chat messages are added to sessions in the form of [Memories](memories). Each session can have many messages +associated with it. + +The `SessionID` is a string key that accepts arbitrary identifiers. Related data you'd like to store can be persisted +as metadata. + + + +## Adding a Session + +`SessionIDs` are arbitrary identifiers that you can map to relevant business objects in your app, such as users or a +conversation a user might have with your app + + +**Sessions don't need to be explicitly created** + +Sessions are created automatically when adding Memories. If the SessionID already exists, then the Memory is upserted into the Session. + +Manually creating a session can be useful if you want to associate it with a user or add metadata. + + + + + + +```python +async with ZepClient(api_key=api_key) as client: + session_id = uuid.uuid4().hex # A new session identifier + session = Session( + session_id=session_id, + user_id=user_id, # Optionally associate this session with a user + metadata={"foo" : "bar"} + ) + await client.memory.aadd_session(session) +``` + + + + +```typescript +const client: ZepClient = await ZepClient.init(api_key); +const session_id: string = uuid.v4(); // Generate a new session identifier +const session: Session = new Session({ + session_id, + user_id: user.user_id, // Optionally associate this session with a user + metadata: { foo: "bar" }, +}); +await client.memory.addSession(session); +``` + + + + +Looking to associate a Session with a User? Check out our [User Management](/users) docs. + +## Updating Session Metadata + +You can update a session's metadata by providing a Session object with new metadata. Note that +metadata is merged, so any existing metadata will be preserved. + + + + + +```python +session = Session(session_id=session_id, metadata={"qax" : "baz"}) +await client.memory.aupdate_session(session) +``` + + + + +```typescript +const session: Session = new Session({ + session_id, + metadata: { qax: "baz" }, +}); +await client.memory.updateSession(session); +``` + + + + +## Getting a Session + + + + + +```python +session = await client.memory.aget_session(session_id) +print(session.dict()) +``` + + + + +```typescript +const session = await client.memory.getSession(session_id); +console.log(session); +``` + + + + +## Deleting a Session + +Deleting a Session soft-deletes the Session and all associated Memories. The Session and Memories are still available in +the database, but are marked as deleted and will not be returned in search results. + +If you persist memory to a deleted Session, it will be undeleted. Deleted Memories will, however, remain deleted. + +Soft-deleted data is hard-deleted periodically. + + + + + +```python +await client.memory.adelete_memory(session_id) +``` + + + + +```typescript +await client.memory.deleteMemory(session_id); +``` + + + + +## Listing Sessions + +You can list all Sessions in the Zep Memory Store with optional limit and cursor parameters for pagination. We also +provider a helper generator function making it simple to iterate over all Sessions. + + + + + +```python +# List the first 10 Sessions +sessions = client.memory.list_sessions(limit=10, cursor=0) +for session in sessions: + print(session) + +# List All Sessions using a generator +all_sessions = client.memory.list_all_sessions(chunk_size=100) +for session_chunk in all_sessions: + for session in session_chunk: + print(session) +``` + + + + +```typescript +// List the first 10 Sessions +const sessions: Session[] = await client.memory.listSessions(10, 0); +console.log("First 10 Sessions:"); +sessions.forEach((session) => console.log(session)); + +// List All Sessions using a generator +console.log("All Sessions:"); +const allSessionsGenerator = client.memory.listSessionsChunked(100); +for await (const sessionChunk of allSessionsGenerator) { + sessionChunk.forEach((session) => console.log(session)); +} +``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/summaries.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/summaries.mdx new file mode 100644 index 00000000000..49db20b89e8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/chat-history-memory/summaries.mdx @@ -0,0 +1,35 @@ +--- +slug: chat-history-memory/summaries +--- + +A summary for the last or trailing `N` messages is generated on every new message added to a Session. `N` is the +configured +`Message Window` for your [Zep Project](/getting-started/projects). The first summary is generated after the first `N` messages are +added to a Session. + + +**Summaries are generated asynchronously.** + +Zep uses a proprietary, low-latency summarization model to generate dialog summaries asynchronously. This means that +summary generation does not delay your requests to retrieve Memories from Zep. + + + + + List of Summaries + + +## Accessing Summaries + +Summaries are added to Memory responses when you retrieve a Session's [Memory](memories), enabling your prompt to be +populated with both the most recent messages and a summary the more distant history. + +### Perpetual Memory + +Zep searches for summaries relevant to the current conversation, summaries high-scoring summaries that are returned from +search and includes this summary in your Memory request. + +### Message Window Buffer Memory + +Zep automatically determines which summary to add to a response based on your configured `Message Window` and the number +of messages in the Session. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/dialog-classification.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/dialog-classification.mdx new file mode 100644 index 00000000000..45874684460 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/dialog-classification.mdx @@ -0,0 +1,180 @@ +--- +title: Dialog Classification +slug: dialog-classification +--- + + + + Zep enables you to classify Sessions into various categories and save these classifications in the Session's metadata. + This feature is handy for directing Sessions to appropriate agents or for monitoring the kinds of interactions users + have with your Assistant. Classifications are processed in just a few hundred milliseconds, allowing them to run + synchronously with the chat loop. + +## Classifying Sessions + +A classification task consists of a topic and a list of classes. +The `topic` is the type of classification you want to perform, and the `classes` are the possible categories you want to classify the Session into. +The `topic` is only used as a name or label for the classification task and does not affect the classification itself. + +You may optionally specify the number of previous messages to consider when classifying the Session (default `4`), and whether to persist the classification in the Session's metadata (default `True`). + + + + ```python classes = ["class1", "class2", ..., "other"] classification = client.memory.classify_session( session_id, + "topic", classes, last_n=4, persist=True ) ``` + + + ```typescript const classes = ["class1", "class2", ..., "other"] const classification = await + client.memory.classifySession( session_id, "topic", classes, 4, // lastN true // persist ) ``` + + + +The returned result will be one of the classes you provided, or "other" if none of the classes are a good fit. The classification result is also stored in the Session's metadata if `persist` is `True`. + +```json +{ "system": { "classes": { "topic": "travel" } } } +``` + +And Sessions viewed in the Zep app will be labeled with the classification result. + + + Classifier Metadata Image + + +### Adding Custom Instruction + +You may optionally provide a custom instruction to the classification task. This instruction will be injected into the Dialog Classification Prompt. + +You may want to use this option if you have specific guidelines that you want to communicate to the LLM in addition to our classification prompt. + + + + ```python classes = ["advanced", "beginner", ..., "dojo"] classification = client.memory.classify_session( + session_id, "experience level", classes, instruction="Classify the user's experience level. You may classify + experience as 'dojo' only if the user is a black belt." ) ``` + + + ```typescript const classes = ["advanced", "beginner", ..., "dojo"] const classification = await + client.memory.classifySession( session_id, "experience level", classes, "Classify the user's experience level. You + may classify experience as 'dojo' only if the user is a black belt." ) ``` + + + +### Building a Semantic Router with User Intent + +Zep's Session Classifier can be used to build a semantic router that routes user sessions to different agents or chains based on the user's intent. + +```Text +user: Hello, my phone isn't responding to touch. +``` + + + + ```python + classes = [ + "sales interest", + "needs support", + "has payment question", + "other" + ] + classification = client.memory.classify_session( + session_id, "intent", classes + ) + + print(classification) + ``` + + + ```typescript + const classes = [ + "sales interest", + "needs support", + "has payment question", + "other" + ] + +const classification = await client.memory.classifySession( +session_id, "intent", classes +) + + console.log(classification.class) + ``` + + + +```Text +"needs support" +``` + +### A High-Performance Tool Picker + +Using an agent to pick tools can often be slow and inaccurate. Zep's Session Classifier allows you to pick tools at very low latency +and high accuracy. You may then instruct an LLM to use the selected tool and provided Session information. + +```Text +user: What is the capital of France? +``` + + + + ```python classes = [ "complete math problems using a calculator", "research topics or find information with a web + search", "no matching tool", ] classification = client.memory.classify_session( session_id, "tool", classes ) ``` + + + ```typescript const classes = [ "complete math problems using a calculator", "research topics or find information + with a web search", "no matching tool", ] const classification = await client.memory.classifySession( session_id, + "tool", classes ) ``` + + + +```Text +"research topics or find information with a web search" +``` + +### Classifying Emotions + +Zep's Session Classifier can be used to classify user emotions and store these classifications in the Session's metadata. + +```Text +AI: We're unfortunately going to reschedule your appointment. +user: Is that entirely necessary? I'm very busy. +``` + + + + ```python classes = [ "happy", "sad", "frustrated", "angry", "other" ] classification = + client.memory.classify_session( session_id, "emotion", classes ) ``` + + + ```typescript const classes = [ "happy", "sad", "frustrated", "angry", "other" ] classification = await + client.memory.classifySession( session_id, "emotion", classes ) ``` + + + +```Text +"frustrated" +``` + +## Executing Multiple Classifications + +Zep supports executing multiple classification tasks against a Session, allowing you to classify a Session into different categories and store these classifications in the Session's metadata. + +```json +{ + "system": { + "classes": { + "topic": "travel", + "intent": "needs support", + "emotion": "frustrated" + } + } +} +``` + +## Best Practices + +1. **Use a small number of classes**: The more classes you have, the more difficult it is to classify a Session accurately. Recommendation: no more than 5 or 6 classes. +2. **Ensure your classes are well separated**: If your classes are too similar, the classifier will have a hard time distinguishing between them. +3. **Provide a "none", "other", or "unknown" class**: If none of the classes are a good fit, the classifier should be able to select an option that indicates this. +4. **Limit the number of previous messages**: The more messages you consider, the longer the classification will take. Additionally, the context may change through the conversation. Recommendation: no more than the prior 4 to 6 messages. +5. **Persist the classification**: If you want to use the classification result in the future, you should persist the classification in the Session's metadata. This is the default. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/docs.yml b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/docs.yml new file mode 100644 index 00000000000..4484474f4c3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/docs.yml @@ -0,0 +1,110 @@ +instances: [] +title: Zep Documentation +favicon: logo/favicon.png +logo: + light: logo/zep-name-logo-gradient.svg + dark: logo/zep-name-logo-pink.svg + height: 28 +colors: + accentPrimary: + dark: '#D5479D' + light: '#D5479D' + background: {} +tabs: + api-reference: + slug: api-reference + displayName: API Reference + documentation: + displayName: Documentation + slug: documentation +navigation: + - tab: api-reference + layout: + - section: document + contents: [] + - section: collection + contents: [] + - section: session + contents: [] + - section: memory + contents: [] + - section: messages + contents: [] + - section: search + contents: [] + - section: user + contents: [] + - tab: documentation + layout: + - section: Getting Started + contents: + - page: SDK Installation + path: sdks.mdx + - page: '' + path: projects.mdx + - page: '' + path: users.mdx + - section: Chat History Memory + contents: + - page: Overview + path: chat-history-memory/overview.mdx + - page: Memories + path: chat-history-memory/memories.mdx + - page: '' + path: chat-history-memory/sessions.mdx + - page: '' + path: chat-history-memory/messages.mdx + - page: '' + path: chat-history-memory/summaries.mdx + - page: Vector Search Over Chat History + path: chat-history-memory/search.mdx + - section: Dialog Tools + contents: + - page: Dialog Classification + path: dialog-classification.mdx + - page: Question Synthesis + path: question-synthesis.mdx + - section: Working with Search + contents: + - page: Constructing Search Queries + path: working-with-search.mdx + - section: Document Collections + contents: + - page: Document Collections + path: document-collections.mdx + - section: Ecosystem + contents: + - section: LangChain + contents: + - page: Overview + path: langchain/overview.mdx + - page: ZepChatMessageHistory + path: langchain/messagehistory.mdx + - page: ZepVectorStore + path: langchain/vectorstore.mdx + - section: Examples + contents: + - page: MessageHistory Example + path: langchain/examples/messagehistory-example.mdx + - page: VectorStore Example + path: langchain/examples/vectorstore-example.mdx + - page: RAG + Message History example + path: langchain/examples/rag-message-history-example.mdx + - page: Flowise + path: ecosystem/flowise.mdx + - page: Chainlit + path: ecosystem/chainlit.mdx + - page: LlamaIndex + path: ecosystem/llamaindex.mdx + - page: LangFlow + path: ecosystem/langflow.mdx + - page: n8n + path: ecosystem/n8n.mdx + - section: Legal + contents: + - page: Privacy Policy + path: legal/privacy-policy.mdx + - page: Terms of Service + path: legal/terms-of-service.mdx + - page: Website Terms of Use + path: legal/website-terms-of-use.mdx diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/document-collections.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/document-collections.mdx new file mode 100644 index 00000000000..8510515dbea --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/document-collections.mdx @@ -0,0 +1,502 @@ +--- +title: Document Collections +slug: document-collections +--- + + +Zep's document vector store lets you embed and search documents using vector similarity search, Maximum Marginal Relevance Re-Ranking, and metadata filtering. + +You can manage collections, ingest documents, and search using _Zep's SDKs_, _LangChain_, or _LlamaIndex_. + + + `zep-python` supports async operations. + + All methods come in sync and async flavors, with async methods prefixed with `a`. + + For instance, `zep-python` offers both `zep_client.memory.add_memory` and `zep_client.memory.aadd_memory`. + + + +## Key Concepts + +### Collections + +A `Collection` is a group of documents that use the same embedding strategy and model. _Zep automatically creates embeddings_ for the documents you provide. + +### Documents + +`Documents` are the texts you want to embed and search. You can add documents to collections and optionally assign them a unique ID and metadata. _If you add metadata, it can help filter search results._ + +## Initializing the Zep Client + +For details on initializing the Zep client, check out the [SDK documentation](sdks). + +## Creating a Collection + + + + + +```python +client = ZepClient(api_key="zep_api_key") + +collection_name = "babbagedocs" # the name of your collection. alphanum values only + +collection = client.document.add_collection( + name=collection_name, # required + description="Babbage's Calculating Engine", # optional + metadata={"foo": "bar"}, # optional metadata to associate with this collection +) +``` + + + + + +```typescript +const client = await ZepClient.init("zep_api_key"); + +const collection_name: string = "babbagedocs"; // The name of your collection. Alphanumeric values only + +const collection = await client.document.addCollection({ + name: collection_name, // Required + description: "Babbage's Calculating Engine", // Optional + metadata: { foo: "bar" }, // Optional metadata to associate with this collection +}); +``` + + + + + +## Loading an Existing Collection + + + + + +```python +collection = client.document.get_collection(collection_name) +``` + + + + + +```typescript +const collection = await client.document.getCollection(collection_name); +``` + + + + + +## Adding Documents to a Collection + + + + + +```python +chunks = read_chunks_from_file(file, max_chunk_size) # your custom function to read chunks from a file + +documents = [ + Document( + content=chunk, + document_id=f"{collection_name}-{i}", # optional document ID + metadata={"bar": i}, # optional metadata + ) + for i, chunk in enumerate(chunks) +] + +uuids = collection.add_documents(documents) +``` + +`document_id` is an optional identifier you can assign to each document. It's handy for linking a document chunk with a specific ID you choose. + +The `metadata` is an optional dictionary that holds metadata related to your document. Zep leverages this metadata for hybrid searches across a collection, enabling you to filter search results more effectively. + +When you use `collection.add_documents`, it returns a list of Zep UUIDs corresponding to the documents you've added to the collection. + + + + + +```typescript +const chunks: string[] = read_chunks_from_file(file, max_chunk_size); // Your custom function to read chunks from a file + +const documents: Document[] = chunks.map((chunk, index) => { + return new Document({ + content: chunk, + documentId: `${collection_name}-${index}`, // Optional document ID + metadata: { bar: index }, // Optional metadata + }); +}); + +const uuids: string[] = await client.document.addDocuments(collection_name, documents); +``` + +`document_id` is an optional identifier you can assign to each document. It's handy for linking a document chunk with a specific ID you choose. + +The `metadata` is an optional dictionary that holds metadata related to your document. Zep leverages this metadata for hybrid searches across a collection, enabling you to filter search results more effectively. + +When you use `collection.addDocuments`, it returns a list of Zep UUIDs corresponding to the documents you've added to the collection. + + + + + +Zep's document vector store has VectorStore support for Langchain. + +**Python Example:** + +```python +from langchain.docstore.base import Document +from langchain.text_splitter import RecursiveCharacterTextSplitter +from zep_python.langchain import ZepVectorStore + +vectorstore = ZepVectorStore(collection) + +text_splitter = RecursiveCharacterTextSplitter( + chunk_size=400, + chunk_overlap=50, + length_function=len, +) + +docs = text_splitter.create_documents([raw_text]) +uuids = vectorstore.add_documents(docs) +``` + +**Typescript Example:** + +```typescript +import { ZepClient } from "@getzep/zep-js"; +import { ZepVectorStore } from "@getzep/zep-js/langchain"; +import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; + +const vectorStore = await ZepVectorStore.init({ + client: zepClient, + collectionName: "", +}); +const text_splitter = new RecursiveCharacterTextSplitter({ + chunkSize: 400, + chunkOverlap: 50, + lengthFunction: (text: string) => text.length, // Assuming lengthFunction accepts a function that returns the length of the text +}); + +const docs = await text_splitter.createDocuments([raw_text]); +const uuids = await vectorstore.addDocuments(docs); +``` + + + + + +### Chunking your documents + +Choosing the right _chunking strategy_ is crucial and highly dependent on your specific needs. A variety of 3rd-party libraries, including Langchain, offer support for processing documents from numerous sources and dividing them into smaller segments suitable for embedding. + +We recommend experimenting with various extractors, chunking strategies, sizes, and overlaps to discover the optimal approach for your project. + +## Monitoring Embedding Progress + +The process of embedding documents in Zep is asynchronous. To keep track of your collection's embedding progress, you can periodically check the collection's status: + + + + + +```python +import time + +while True: + c = client.document.get_collection(collection_name) + print( + "Embedding status: " + f"{c.document_embedded_count}/{c.document_count} documents embedded" + ) + time.sleep(1) + if c.status == "ready": + break +``` + + + + + +```typescript +while (true) { + const collection = await client.document.getCollection(collectionName); + console.log(`Embedding status: ${c.document_embedded_count}/${c.document_count} documents embedded`); + await sleep(1000); // Sleep for 1 second (1000 milliseconds) + if (collection.status === "ready") { + break; + } +} +``` + + + + + +Once the collection's status changes to `ready`, it means all documents have been successfully embedded and are now searchable. + +## Searching a Collection with Hybrid Vector Search + +Zep enables hybrid vector search across your collections, allowing you to pinpoint the most relevant documents based on semantic similarity. Additionally, you have the option to refine your search by filtering through document metadata. + +You can initiate a search using either a text query or an embedding vector, depending on your needs. + + + Zep's Collection and Memory search support semantic search queries, JSONPath-based metadata filters, and a combination of both. Memory search also supports querying by message creation date. + + Read more about [constructing search queries](working-with-search). + + + + + + + +```python +# search for documents using only a query string +query = "the moon" +results = collection.search(text=query, limit=5) + +# hybrid search for documents using a query string and metadata filter +metadata_query = { + "where": {"jsonpath": '$[*] ? (@.baz == "qux")'}, +} +results = collection.search(text=query, metadata=metadata_query, limit=5) + +# Search by embedding vector, rather than text query +# embedding is a list of floats +results = collection.search( + embedding=embedding, limit=5 +) +``` + +`metadata` is an optional dictionary of [JSONPath filters](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html) used to match on metadata associated with your documents. + +`limit` is an optional integer indicating the maximum number of results to return. + + + + + +```typescript +const query = "the moon"; +let results = await collection.search({ text: query, limit: 5 }); + +// Hybrid search for documents using a query string and metadata filter +const metadataQuery = { + where: { jsonpath: '$[*] ? (@.baz == "qux")' }, +}; +results = await collection.search({ text: query, metadata: metadataQuery, limit: 5 }); + +// Search by embedding vector, rather than text query +// embedding is a list of floats +results = await collection.search({ embedding: embedding, limit: 5 }); +``` + +`metadata` is an optional dictionary of [JSONPath filters](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html) used to match on metadata associated with your documents. + +`limit` is an optional integer indicating the maximum number of results to return. + + + + +**Python Example:** + +```python +query = "What is Charles Babbage best known for?" + +print(f"\nSearching for '{query}'\n") +results = vectorstore.search(query, search_type="similarity", k=5) +print_results(results) + +print(f"\nSearching for '{query}' with MMR reranking\n") +results = vectorstore.search(query, search_type="mmr", k=5) +print_results(results) +``` + +**Typescript Example:** + +```typescript +const query = "What is Charles Babbage best known for?"; + +console.log(`\nSearching for '${query}'\n`); +const resultsSimilarity = await vectorstore.search(query, { searchType: "similarity", k: 5 }); +console.log(resultsSimilarity); + +console.log(`\nSearching for '${query}' with MMR reranking\n`); +const resultsMMR = await vectorstore.search(query, { searchType: "mmr", k: 5 }); +console.log(resultsMMR); +``` + +_You can also use `ZepVectoreStore` as a retriever with langchain_ + +Python example + +```python +retriever = vectorstore.as_retriever() +_inputs = RunnableParallel( + { + "question": lambda x: x["question"], + "context": extract_question | retriever | _combine_documents, + }, +).with_types(input_type=UserInput) + +chain = (_inputs | answer_prompt | ChatOpenAI() | StrOutputParser()) +``` + + **Please check the complete python [rag vector store chain example](https://github.com/getzep/zep-python/blob/main/examples/langchain-langserve/app/rag_vector_store_chain.py)** + +Typescript example + +```typescript +const retriever = vectorStore.asRetriever(); + +const setupAndRetrieval = RunnableMap.from({ + context: new RunnableLambda({ + func: (input: string) => retriever.invoke(input).then(combineDocuments), + }), + question: new RunnablePassthrough(), +}); +const outputParser = new StringOutputParser(); + +const chain = setupAndRetrieval.pipe(prompt).pipe(model).pipe(outputParser); +``` + +> _Please check the complete typescript [rag vector store chain example](https://github.com/getzep/zep-js/blob/main/examples/langchain/vector_store_example.ts)_ + + + + + +## Retrieving Documents by UUID + +Zep supports retrieving a list of documents by Zep UUID: + + + + + +```python +docs_to_get = uuids[40:50] +documents = collection.get_documents(docs_to_get) +``` + + + + + +```typescript +const docsToGet = uuids.slice(40, 50); +const documents = await collection.getDocuments(docsToGet); +``` + + + + + +## Other Common Operations + +This section covers additional common operations you might need to perform, such as listing all collections within your client's scope. The examples above demonstrate how to create an index on a collection and list all collections for both Python and TypeScript. + +### Updating a Collection's Description or Metadata + + + + + +```python +client.document.update_collection( + collection_name, + description="Charles Babbage's Babbage's Calculating Engine 2", + metadata={"newfoo": "newbar"}, +) +``` + + + + +```typescript +await client.document.updateCollection(collectionName, { + description: "Charles Babbage's Babbage's Calculating Engine 2", + metadata: { newfoo: "newbar" }, +}); +``` + + + + + +### Update a Document's ID or Metadata + + + + + +```python +collection.update_document(document_uuid, document_id="new_id", metadata={"foo": "bar"}) +``` + + + + +```typescript +await collection.updateDocument(documentUUID, { documentId: "new_id", metadata: { foo: "bar" } }); +``` + + + + + +### Deleting Documents + +Zep supports deleting documents from a collection by UUID: + + + + + +```python +collection.delete_document(document_uuid) +``` + + + + + +```typescript +await collection.deleteDocument(document_uuid); +``` + + + + + +### Deleting a Collection + +Deleting a collection will delete all documents in the collection, as well as the collection itself. + + + + + +```python +client.document.delete_collection(collection_name) +``` + + + + + +```typescript +await client.document.deleteCollection(collection_name); +``` + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/chainlit.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/chainlit.mdx new file mode 100644 index 00000000000..246811bf995 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/chainlit.mdx @@ -0,0 +1,148 @@ +--- +title: Chainlit +subtitle: >- + Chainlit is an open-source async Python framework which allows developers to + build scalable Conversational AI or agentic applications. +slug: ecosystem/chainlit +--- + + + + You can follow Chainlit installation steps on their + Getting Started Page + + +By integrating Zep into your Chainlit LLM application, you elevate your conversational agent with powerful features like long-term memory and context fusion. + +In this guide, we'll walk you through the steps to build a simple Question and Answer agent using Chainlit, Open AI and Zep. + +### Steps to Use Zep Cloud with ChainLit + +1. **Setup Zep Client**: Initialize the Zep Client within your ChainLit application using your [Zep Project API key](https://help.getzep.com/projects). + +```python +# Import necessary modules from Zep Python SDK and ChainLit. +from zep_python import ZepClient +from zep_python.memory import Memory, Session +from zep_python.message import Message +from zep_python.user import CreateUserRequest +import chainlit as cl +import uuid +import os +from openai import AsyncOpenAI + +# Retrieve API keys from environment variables. +ZEP_API_KEY = os.environ.get("ZEP_API_KEY") +OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") + +# Initialize clients for OpenAI GPT-4 and Zep with respective API keys. +openai_client = AsyncOpenAI(api_key=OPENAI_API_KEY) +zep = ZepClient(api_key=ZEP_API_KEY) +``` + +2. **User and Session Management**: Leverage the `CreateUserRequest` and [`Session`](https://help.getzep.com/chat-history-memory/sessions) models to manage your application's users and sessions effectively. + +```python +@cl.on_chat_start +async def start_chat(): + """Handles the event triggered at the start of a new chat through ChainLit.""" + # Generate unique identifiers for the user and session. + user_id = str(uuid.uuid4()) + session_id = str(uuid.uuid4()) + + # Save user and session identifiers in the current session context. + cl.user_session.set("user_id", user_id) + cl.user_session.set("session_id", session_id) + + # Register a new user in Zep's system using the generated User ID. + await zep.user.aadd(CreateUserRequest(user_id=user_id)) + + # Start a new session for the user in Zep. + await zep.memory.aadd_session(Session(user_id=user_id, session_id=session_id)) +``` + +3. **Zep Dialog tools**: Elevate agent knowledge with ChainLit Steps and Zep Dialog Tools + + Discover more about Zep's dialog tools on the{" "} + Zep Documentation Page. + + +```python +@cl.step(name="session classification", type="tool") +async def classify_session(session_id: str): + """Classify dialog with custom instructions.""" + # Define categories for classification. + classes = [ + "General", + "Travel", + "Shopping", + "Cars", + ] + # Use Zep's dialog async classification feature with custom instruction for session classification. + classification = await zep.memory.aclassify_session( + session_id, "session_classification", classes, persist=True, instruction="you are a helpful assistance, give a conversation classify 0 for General topics, 1 for Travel-related discussions, 2 for Shopping conversations, and 3 for talks about Cars. For example, a chat about visiting Paris for landmarks and cuisine should be classified as 1." + ) + return classification +``` + +4. **Message Handling**: You can effectively store and fetch your Chainlit application chat history on Zep memory store, enhancing your LLM conversational context. + + + Discover more about Zep's memory store capabilities on the{" "} + Zep Documentation Page. + + +```python + +@cl.step(name="OpenAI", type="llm") +async def call_openai(session_id): + """Invokes the OpenAI API to generate a response based on the session message history.""" + # Fetch session messages from Zep. + memory = await zep.message.aget_session_messages(session_id) + memory_history = [m.to_dict() for m in memory] + + # Prepare data, excluding certain fields for privacy/security. + cleaned_data = [{k: v for k, v in item.items() if k not in ['created_at', 'role_type', 'token_count', 'uuid']} for item in memory_history] + + # Generate a response from OpenAI using the cleaned session data. + response = await openai_client.chat.completions.create( + model="gpt-4", + temperature=0.1, + messages=cleaned_data, + ) + return response.choices[0].message + +@cl.on_message +async def on_message(message: cl.Message): + """Processes each incoming message, integrates with OpenAI for response, and updates Zep memory.""" + session_id = cl.user_session.get("session_id") + # classify user message to give the LLM a semantic insights to what the user request is about + classify_sess = await classify_session(session_id) + # Store the incoming message in Zep's session memory and append the classified dialog. + await zep.memory.aadd_memory( + session_id, + Memory(messages=[Message(role_type="user", content=message.content + "\n" + "conversation_topic: " + classify_sess.class_, role="user")]), + ) + + # Retrieve a response from the OpenAI model. + response_message = await call_openai(session_id) + + # Send the generated response back through ChainLit. + msg = cl.Message(author="Answer", content=(response_message.content)) + await msg.send() + + # Update Zep's session memory with the assistant's response for continuity. + await zep.memory.aadd_memory( + session_id, + Memory(messages=[Message(role_type="assistant", content=response_message.content, role="assistant")]), + ) + +``` + +5. To access your LLM session data, navigate to the Zep Cloud Console, select a session, and review all the associated session data and logs. + +Zep Cloud session console example + +In conclusion, integrating Zep Cloud with Chainlit empowers developers to create conversational AI applications that are more intelligent, context-aware, and efficient. + +For additional examples, check out more [use cases](https://github.com/getzep/zep-python/tree/main/examples). diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/flowise.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/flowise.mdx new file mode 100644 index 00000000000..69bda856b9b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/flowise.mdx @@ -0,0 +1,57 @@ +--- +title: Flowise +subtitle: >- + Flowise is a low-code/no-code drag & drop tool with the aim to make it easy + for people to visualize and build LLM apps. +slug: ecosystem/flowise +--- + + + + You can follow Flowise installation steps on their + Getting Started Page + + +## Using Zep Cloud Nodes + +Once you have Flowise app up and running, you can use Zep Cloud nodes in your LLM apps. + +We expose the following nodes: + +- `Zep Memory - Cloud` - wrapper around Zep Memory API. You can use this node to add Long-Term memory to your app. + Learn more about [Zep Memory](/chat-history-memory) + +In order to use this node, you will need to have a Zep Project API key and add it as a Connect Credential in your Flowise app. + +Zep Cloud Memory Flowise Node + +A complete chain using Zep Memory Node, may look like this: + +Zep Cloud Memory Flowise Node Complete Example + +- `Zep Collection - Cloud` - wrapper around Zep’s document vector store. It lets you embed and search documents. You can use this node to add RAG capabilities to your app. + Learn more about [Zep Collections](/document-collections) + +In order to use this node, you will need to have a Zep Project API key and add it as a Connect Credential in your Flowise app. + +You will also need to provide the collection name for your Zep Collection. + +Zep Cloud Collection Flowise Node + +A complete chain using Zep Collection Node, may look like this: + + + Note that we are using a Website Crawler node to load documents about **Marie Curie** into a **Zep Collection**. + You can also connect any other Document Loader available on Flowise platform. + + We are using **Zep Collection Node** as a retriever passed into **Conversational Retrieval QA Chain**. + + This example also illustrates how you can use **Zep Memory** in combination with **Zep Collection**. + + + +Zep Cloud Collection Flowise Node Complete Example diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/langflow.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/langflow.mdx new file mode 100644 index 00000000000..840370464ab --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/langflow.mdx @@ -0,0 +1,6 @@ +--- +title: LangFlow +subtitle: Zep Cloud Integration coming soon +slug: ecosystem/langflow +--- + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/llamaindex.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/llamaindex.mdx new file mode 100644 index 00000000000..fca5f46142b --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/llamaindex.mdx @@ -0,0 +1,6 @@ +--- +title: LlamaIndex +subtitle: Zep Cloud Integration coming soon +slug: ecosystem/llamaindex +--- + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/n8n.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/n8n.mdx new file mode 100644 index 00000000000..a89030c87fc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/ecosystem/n8n.mdx @@ -0,0 +1,6 @@ +--- +title: n8n +subtitle: Zep Cloud Integration coming soon +slug: ecosystem/n8n +--- + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/fern.config.json b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/fern.config.json new file mode 100644 index 00000000000..2e3e1df85fd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/fern.config.json @@ -0,0 +1,4 @@ +{ + "version": "*", + "organization": "fern" +} \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/classifier-metadata.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/classifier-metadata.png new file mode 100644 index 00000000000..88291b8ab07 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/classifier-metadata.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/create_new_project.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/create_new_project.png new file mode 100644 index 00000000000..b52f286d1ae Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/create_new_project.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/fact-table.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/fact-table.png new file mode 100644 index 00000000000..0fd8b722524 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/fact-table.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-collection-node-complete.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-collection-node-complete.png new file mode 100644 index 00000000000..f8f9b3e363a Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-collection-node-complete.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-collection-node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-collection-node.png new file mode 100644 index 00000000000..4d95c1f41dd Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-collection-node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-memory-node-complete.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-memory-node-complete.png new file mode 100644 index 00000000000..9d33d9ae51b Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-memory-node-complete.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-memory-node.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-memory-node.png new file mode 100644 index 00000000000..bb0cde68a84 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/flowise-memory-node.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/perpetual_memory.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/perpetual_memory.png new file mode 100644 index 00000000000..697f62d19c5 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/perpetual_memory.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/session-chainlit-ecosystem.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/session-chainlit-ecosystem.png new file mode 100644 index 00000000000..c7106d4c17d Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/session-chainlit-ecosystem.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/summaries.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/summaries.png new file mode 100644 index 00000000000..862a37d4786 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/images/summaries.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/messagehistory-example.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/messagehistory-example.mdx new file mode 100644 index 00000000000..f3beb9fdc3e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/messagehistory-example.mdx @@ -0,0 +1,249 @@ +--- +title: MessageHistory Example +slug: langchain/examples/messagehistory-example +--- + + +The Zep Python SDK includes the `ZepChatMessageHistory` class, compatible with [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/expression_language/get_started). + +This guide will walk you through creating a [MessageHistory](/chat-history-memory) chain using Zep's conversation history. + +**You can generate a project api key in [Zep Dashboard](https://app.getzep.com/projects).** + + + Make sure you have the following environment variables specified when running these examples: + + `ZEP_API_KEY` - API key to your zep project + + `OPENAI_API_KEY` - Open AI api key which the chain will require to generate the answer + + + + + **You will need to have a collection in place to initialize vector store in this example** + + If you want to create a collection from a web article, + you can run the [python ingest script](https://github.com/getzep/zep-python/blob/main/examples/langchain-langserve/app/ingest.py) + Try modifying the script to ingest the article of your choice. + + Alternatively, you can create a collection by running either [Document example](https://github.com/getzep/zep-python/blob/main/examples/documents/documents_async.py) + in python sdk repository or [Document example](https://github.com/getzep/zep-js/blob/main/examples/documents/index.ts) in typescript sdk repository. + + + + + **You will need to have a `session_id` in place to invoke the final chain in this example** + + You can create a session by running either [Memory example](https://github.com/getzep/zep-python/blob/main/examples/chat_history/memory_async.py) + + in python sdk repository or [Memory example](https://github.com/getzep/zep-js/blob/main/examples/memory/memory_example.ts) in typescript sdk repository. + + + +**Initialize ZepClient with necessary imports** + + + + + +```python +import os +from typing import List, Tuple + +from langchain_core.output_parsers import StrOutputParser +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +from langchain_core.pydantic_v1 import BaseModel, Field +from langchain_core.runnables import ( + RunnableParallel, +) +from langchain_core.runnables.history import RunnableWithMessageHistory +from langchain_openai import ChatOpenAI + +from zep_python import ZepClient +from zep_python.langchain import ZepChatMessageHistory + +zep = ZepClient( + api_key=ZEP_API_KEY, +) +``` + + + + + +```typescript +import { ZepClient } from "@getzep/zep-js"; +import { ZepChatMessageHistory } from "@getzep/zep-js/langchain"; +import { ChatOpenAI } from "@langchain/openai"; +import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; +import { RunnableWithMessageHistory } from "@langchain/core/runnables"; + +const zepClient = await ZepClient.init(process.env.ZEP_API_KEY); +``` + + + + + + + + +```python +template = """Answer the question below as if you were a 19th centry poet: + """ +answer_prompt = ChatPromptTemplate.from_messages( + [ + ("system", template), + MessagesPlaceholder(variable_name="chat_history"), + ("user", "{question}"), + ] +) +``` + + + + +```typescript +const prompt = ChatPromptTemplate.fromMessages([ + ["system", "Answer the user's question below. Be polite and helpful:"], + new MessagesPlaceholder("history"), + ["human", "{question}"], +]); +``` + + + + +**Set up an answer synthesis template and prompt.** + + + **`MessagesPlaceholder`** - We're using the variable name `chat_history` here. + + This will incorporate the chat history into the prompt. + + It's **important** that this variable name aligns with the `history_messages_key` in the `RunnableWithMessageHistory` chain for seamless integration. + + + + **`question` must match `input_messages_key` in `RunnableWithMessageHistory`` chain.** + +**Compose the final chain** + + + + + +```python +inputs = RunnableParallel( + { + "question": lambda x: x["question"], + "chat_history": lambda x: x["chat_history"], + }, +) + +chain = RunnableWithMessageHistory( + inputs | answer_prompt | ChatOpenAI() | StrOutputParser(), + lambda session_id: ZepChatMessageHistory( + session_id=session_id, # This uniquely identifies the conversation + zep_client=zep, + memory_type="perpetual", + ), + input_messages_key="question", + history_messages_key="chat_history", +) +``` + + + + +```typescript +const chain = prompt.pipe( + new ChatOpenAI({ + temperature: 0.8, + modelName: "gpt-3.5-turbo-1106", + }), +); + +const chainWithHistory = new RunnableWithMessageHistory({ + runnable: chain, + getMessageHistory: (sessionId) => + new ZepChatMessageHistory({ + client: zepClient, + sessionId: sessionId, + memoryType: "perpetual", + }), + inputMessagesKey: "question", + historyMessagesKey: "history", +}); +``` + + + + +Here's a quick overview of what's happening: + +1. We use `RunnableWithMessageHistory` to incorporate [Zep's Chat History](/chat-history-memory) into our chain. +2. This class requires a `session_id` as a parameter when you activate the chain. +3. To manually invoke this chain, provide the `session_id` as a parameter and the `question` as an input to the chain. + + + + + +```python +chain_with_history.invoke( + {"question": "-"}, + config={"configurable": {"session_id": "-"}}, +) +``` + + + + +```typescript +await chainWithHistory.invoke( + { + question: "-", + }, + { + configurable: { + sessionId: "-", + }, + }, +); +``` + + + + +First, we initialize `ZepChatMessageHistory` with the following parameters: + +1. `session_id` - This uniquely identifies the conversation within Zep. +2. `zep_client` - The instance of the Zep client. +3. `memory_type` set to `perpetual`. If not specified, Message Window Buffer Memory will be used by default. We recommend configuring your application to use Perpetual Memory. + +Interested in learning more? [Explore How Zep Memory Works](/chat-history-memory). + +Next, we construct a chain that operates after retrieving the chat history: + +1. `inputs` will extract the user's question and chat history from the context. +2. `answer_prompt` will incorporate chat history into the prompt. +3. `ChatOpenAI` will generate a response. +4. `StrOutputParser` will parse the response. + +## Running the Chain with LangServe + +This chain can also be executed as part of our LangServe sample project. To do this, you'll need to: + +For this you will need to: + +Clone our [Python SDK](https://github.com/getzep/zep-python) + +```bash +git clone https://github.com/getzep/zep-python +cd examples/langchain-langserve +``` + +There is a [README](https://github.com/getzep/zep-python/blob/main/examples/README.md) file in the `langchain-langserve` directory will guide you through the setup process. + +Go to `http://localhost:8000/message_history/playground` to use LangServe playground for this chain. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/rag-message-history-example.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/rag-message-history-example.mdx new file mode 100644 index 00000000000..ffc2765a94a --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/rag-message-history-example.mdx @@ -0,0 +1,33 @@ +--- +title: RAG + Message History example +slug: langchain/examples/rag-message-history-example +--- + + +**You can generate a Project API Key in [Zep Dashboard](https://app.getzep.com/projects).** + + + Make sure you have the following environment variables specified when running these examples: + + `ZEP_API_KEY` - API key to your zep project + + `OPENAI_API_KEY` - Open AI api key which the chain will require to generate the answer + + + + + **You will need to have a `session_id` in place to invoke the final chain in this example** + + You can create a session by running either [Memory example](https://github.com/getzep/zep-python/blob/main/examples/chat_history/memory_async.py) + + in python sdk repository or [Memory example](https://github.com/getzep/zep-js/blob/main/examples/memory/memory_example.ts) in typescript sdk repository. + + + +You can find a complete RAG + Message History examples in the SDK examples + +They showcase how to use the `ZepChatMessageHistory` and `ZepVectorStore` classes as part of one chain. + +[Python RAG + Message history example](https://github.com/getzep/zep-python/tree/main/examples/langchain-langserve) + +[Typescript RAG + Message history example](https://github.com/getzep/zep-js/blob/main/examples/langchain/message_history_vector_store_example.ts) diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/vectorstore-example.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/vectorstore-example.mdx new file mode 100644 index 00000000000..084e887d0cc --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/examples/vectorstore-example.mdx @@ -0,0 +1,336 @@ +--- +title: VectorStore Example +slug: langchain/examples/vectorstore-example +--- + + +Zep Python SDK ships with `ZepVectorStore` class which can be used with [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/expression_language/get_started) + +Let's explore how to create a RAG chain using the `ZepVectorStore` for semantic search. + +**You can generate a project api key in [Zep Dashboard](https://app.getzep.com/projects).** + + + Before diving into these examples, please ensure you've set the following environment variables: + + `ZEP_API_KEY` - API key to your zep project + + `OPENAI_API_KEY` - Open AI api key which the chain will require to generate the answer + + + + + **You will need to have a collection in place to initialize vector store in this example** + + If you want to create a collection from a web article, + you can run the [python ingest script](https://github.com/getzep/zep-python/blob/main/examples/langchain-langserve/app/ingest.py) + Try modifying the script to ingest the article of your choice. + + Alternatively, you can create a collection by running either [Document example](https://github.com/getzep/zep-python/blob/main/examples/documents/documents_async.py) + in python sdk repository or [Document example](https://github.com/getzep/zep-js/blob/main/examples/documents/index.ts) in typescript sdk repository. + + + + + + + +```python +ZEP_API_KEY = os.environ.get("ZEP_API_KEY") +ZEP_COLLECTION_NAME = os.environ.get("ZEP_COLLECTION") +``` + + + + +```typescript +const ZEP_API_KEY = process.env.ZEP_API_KEY; +const ZEP_COLLECTION_NAME = process.env.ZEP_COLLECTION; +``` + + + + +**Need a project API key? Create one from the [Zep Dashboard](https://app.getzep.com/projects).** + +Initialize ZepClient with necessary imports + + + + + +```python +import os +from typing import List + +from langchain.schema import format_document +from langchain_core.documents import Document +from langchain_core.output_parsers import StrOutputParser +from langchain_core.prompts import ChatPromptTemplate +from langchain_core.prompts.prompt import PromptTemplate +from langchain_core.pydantic_v1 import BaseModel +from langchain_core.runnables import ( + ConfigurableField, + RunnableParallel, +) +from langchain_core.runnables.utils import ConfigurableFieldSingleOption +from langchain_openai import ChatOpenAI + +from zep_python import ZepClient +from zep_python.langchain import ZepVectorStore + +zep = ZepClient( + api_key=ZEP_API_KEY, +) +``` + + + + +```typescript +import { ZepClient } from "@getep/zep-js"; +import { ChatOpenAI } from "@langchain/openai"; +import { BasePromptTemplate, ChatPromptTemplate, PromptTemplate } from "@langchain/core/prompts"; +import { ZepVectorStore, formatDocument } from "@getep/zep-js/langchain"; +import { Document } from "@langchain/core/documents"; +import { RunnableMap, RunnableLambda, RunnablePassthrough } from "@langchain/core/runnables"; +import { StringOutputParser } from "@langchain/core/output_parsers"; +import { ConsoleCallbackHandler } from "@langchain/core/tracers/console"; + +const zepClient = await ZepClient.init(ZEP_API_KEY); +``` + + + + +Initialize ZepVectorStore + + + + + +```python +vectorstore = ZepVectorStore( + collection_name=ZEP_COLLECTION_NAME, + zep_client=zep, +) +``` + + + + +```typescript +const vectorStore = await ZepVectorStore.init({ + client: zepClient, + collectionName: ZEP_COLLECTION_NAME, +}); +``` + + + + +Let's set up the retriever. We'll use `vectorstore` for this purpose and configure it to use [MMR](Search.md) search result reranking. + + + + + +```python +retriever = vectorstore.as_retriever() +``` + + + + + +```typescript +const retriever = vectorStore.asRetriever(); +``` + + + + +Create a prompt template for synthesizing answers. + + + + + +```python +template = """Answer the question based only on the following context: + + {context} + """ +answer_prompt = ChatPromptTemplate.from_messages( + [ + ("system", template), + ("user", "{question}"), + ] +) +``` + + + + +```python +const prompt = ChatPromptTemplate.fromMessages([ + [ + "system", + `Answer the question based only on the following context: + {context}`, + ], + [ + "human", + "{question}" + ], +]); +``` + + + + +Create the default document prompt and define the helper function for merging documents. + + + + + +```python +DEFAULT_DOCUMENT_PROMPT = PromptTemplate.from_template(template="{page_content}") + +def _combine_documents( + docs: List[Document], + document_prompt: PromptTemplate = DEFAULT_DOCUMENT_PROMPT, + document_separator: str = "\n\n", +): + doc_strings = [format_document(doc, document_prompt) for doc in docs] + return document_separator.join(doc_strings) +``` + + + + +```typescript +const DEFAULT_DOCUMENT_PROMPT = PromptTemplate.fromTemplate("{pageContent}"); + +async function combineDocuments( + docs: Document[], + documentPrompt: BasePromptTemplate = DEFAULT_DOCUMENT_PROMPT, + documentSeparator: string = "\n\n", +) { + const docStrings: string[] = await Promise.all( + docs.map((doc) => { + return formatDocument(doc, documentPrompt); + }), + ); + return docStrings.join(documentSeparator); +} +``` + + + + + + + +Let's set up user input and the context retrieval chain. + +```python +# User input +class UserInput(BaseModel): + question: str + +inputs = RunnableParallel( + {"question": lambda x: x["question"], "context": retriever | _combine_documents}, +).with_types(input_type=UserInput) +``` + + + +Define context retriever chain with output parser + +```typescript +const outputParser = new StringOutputParser(); + +const setupAndRetrieval = RunnableMap.from({ + context: new RunnableLambda({ + func: (input: string) => retriever.invoke(input).then(combineDocuments), + }), + question: new RunnablePassthrough(), +}); +``` + + + + +Compose final chain + + + + + +```python +chain = inputs | answer_prompt | ChatOpenAI() | StrOutputParser() +``` + + + + +```typescript +const chain = setupAndRetrieval + .pipe(prompt) + .pipe(model) + .pipe(outputParser) + .withConfig({ + callbacks: [new ConsoleCallbackHandler()], + }); // Optional console callback handler if you want to see input and output of each step in the chain +``` + + + + +Here's a quick rundown of how the process works: + +1. `inputs` grabs the user's question and fetches relevant document context to add to the prompt. +2. `answer_prompt` then takes this context and question, combining them in the prompt with instructions to answer the question using only the provided context. +3. `ChatOpenAI` calls an OpenAI model to generates an answer based on the prompt. +4. Finally, `StrOutputParser` extracts the LLM's result into a string. + +To invote this chain manually, simply pass the `question` into the chain's input. + + + + + +```python +chain_with_history.invoke( + {"question": "-"}, +) +``` + + + + +```typescript +const result = await chain.invoke("-"); // Pass the question as input +``` + + + + +## Running the Chain with LangServe + +You can run this chain, along with others, using our LangServe sample project. + +Here's what you'll need to do: + +Clone our [Python SDK](https://github.com/getzep/zep-python) + +```bash +git clone https://github.com/getzep/zep-python +cd examples/langchain-langserve +``` + +Review the [README](https://github.com/getzep/zep-python/blob/main/examples/README.md) in the `langchain-langserve` directory for setup instructions. + +After firing up the server, head over to `http://localhost:8000/rag_vector_store/playground` to explore the LangServe playground using this chain. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/messagehistory.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/messagehistory.mdx new file mode 100644 index 00000000000..258848b2c02 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/messagehistory.mdx @@ -0,0 +1,83 @@ +--- +title: ZepChatMessageHistory +slug: langchain/messagehistory +--- + + + + **You can find a complete [Message History Example](examples/messagehistory-example) that showcases how to use the + `ZepChatMessageHistory` class in the chain.** + + + + + + + The Zep Python SDK includes the `ZepChatMessageHistory` class, compatible with [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/expression_language/get_started). + + It expects the following initialization parameters: + + + The unique identifier for Zep Session. + If a session for the provided identifier doesn't exist, one will be created + + + + The Zep client used for making API requests. + You can pass it rather than the API Key. + + + + Zep Project API Key. + You can pass it rather than `zep_client` and we will initialize `ZepClient` for you. + + + + The type of memory to use. + Can be `"message_window"` (the default value if None provided) or `"perpetual"`. + You can find out more about the memory types in the [Chat History documentation](Chat-Histories.md). + + + + ```python Initialization Example + chat_history = ZepChatMessageHistory( + session_id=session_id, + zep_client=client, + memory_type="perpetual", + ) + ``` + + + + + The Zep JS SDK includes the `ZepChatMessageHistory` class, compatible with [LangChain Expression Language (LCEL)](https://js.langchain.com/docs/expression_language/). + + It expects the following initialization parameters: + + + The unique identifier for Zep Session. + If a session for the provided identifier doesn't exist, one will be created. + + + + The Zep client used for making API requests. + You can pass it rather than the API Key. + + + + The type of memory to use. + Can be `"message_window"` or `"perpetual"`. + You can find out more about the memory types in the [Chat History documentation](Chat-Histories.md). + + + + ```typescript Initialization Example + const history = new ZepChatMessageHistory({ + client: zepClient, + sessionId: sessionId, + memoryType: "perpetual", + }) + ``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/overview.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/overview.mdx new file mode 100644 index 00000000000..e115273b7bb --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/overview.mdx @@ -0,0 +1,34 @@ +--- +title: Overview +slug: langchain/overview +--- + + +Zep's [Python](https://github.com/getzep/zep-python) and [Typescript](https://github.com/getzep/zep-js) SDKs ship with [ZepVectorStore](vectorstore) and [ZepChatMessageHistory](messagehistory) classes that are compatible with Langchain's JS and Python Expression Languages. + +### Managing Chat History Memory + +Zep's `ZepChatMessageHistory` class can be used to provide long-term memory for your LangChain chat apps or agents. Zep will store the entire historical message stream, and automatically enrich chat sessions. + +You can also provide your bot or agent with access to relevant messages in long-term storage by using Zep's built-in search. + +### Building Retrieval Augmented Generation Apps (Q&A over Docs) + +Zep's `ZepVectorStore` class can be used to store a collection of documents, metadata, and related embeddings. Retrieval Augmented Generation (RAG) apps can then use Zep's vector search to surface documents relevant to a prompt. + +Zep will automatically embed the documents using low-latency local models, ensuring that your app is fast and responsive. + +## Using Zep as a VectorStore and Document Retriever + +See [Python and Typescript examples](examples/vectorstore-example) of how to use **ZepVectorStore** with LangChain Expression Language in your application. + +## Using Zep as a LangChain Memory Store + +See [Python and Typescript examples](examples/messagehistory-example) of how to use **ZepChatMessageHistory** with LangChain Expression Language in your application. + + + **Want to use `ZepChatMessageHistory` together with `ZepVectorStore`?** + + Please check our complete [Python and Typescript examples](examples/rag-message-history-example) + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/vectorstore.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/vectorstore.mdx new file mode 100644 index 00000000000..367d8a91681 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/langchain/vectorstore.mdx @@ -0,0 +1,86 @@ +--- +title: ZepVectorStore +slug: langchain/vectorstore +--- + + + + **You can find a complete [Vector Store Example](examples/vectorstore-example) that showcases how to use the + `ZepVectorStore` class in the chain.** + + + + + + + The Zep Python SDK includes the `ZepVectorStore` class, compatible with [LangChain Expression Language (LCEL)](https://python.langchain.com/docs/expression_language/get_started). + + It expects the following initialization parameters: + + + The name of the collection in the Zep Store. + + + + The description of the collection. + + + + The metadata to associate with the collection. + + + + The Zep client used for making API requests. You can pass it rather than the API Key. + + + + Zep Project API Key. You can pass it rather than `zep_client` and we will initialize `ZepClient` for you. + + + + ```python Initialization Example + vectorstore = ZepVectorStore( + collection_name=ZEP_COLLECTION_NAME, + zep_client=zep, + ) + ``` + + + + + The Zep JS SDK includes the `ZepVectorStore` class, compatible with [LangChain Expression Language (LCEL)](https://js.langchain.com/docs/expression_language/). + + It expects the following initialization parameters: + + + The name of the collection in the Zep Store. + + + + The description of the collection. + + + + The metadata to associate with the collection. + + + + The Zep client used for making API requests. + + + + ```typescript Initialization Example + const vectorStore = await ZepVectorStore.init({ + client: zepClient, + collectionName: "testcollection", + }); + ``` + + **We also expose `fromDocuments` and `fromTexts` static methods to initialize `ZepVectorStore` extending Langchain's `VectorStore` interface.** + + In order to initialize using these methods instead of `init`, make sure you are passing + `FakeEmbeddings` as `embeddings` argument as we don't expose the ability to provide your own embeddings on Cloud Version of our SDK. + + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/privacy-policy.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/privacy-policy.mdx new file mode 100644 index 00000000000..647118503be --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/privacy-policy.mdx @@ -0,0 +1,198 @@ +--- +title: Privacy Policy +slug: legal/privacy-policy +--- + + +**Version 1.0** + +**Last revised on: January 27^th^, 2024** + +Zep Software, Inc. (the "Company") is committed to maintaining robust +privacy protections for its users.  Our Privacy Policy ("Privacy +Policy") is designed to help you understand how we collect, use and +safeguard the information you provide to us and to assist you in making +informed decisions when using our Service. + +For purposes of this Agreement, "Site" refers to the Company's website +properties, which can be accessed at the getzep.com Internet domain. + +"Service" refers to the Company's services accessed via the Site, in +which users can view Company marketing material, register for the +Company's services, access support and help resources, and other +services and resources that may be made available from time to time. + +The terms "we," "us," and "our" refer to the Company. + +"You" refers to you, as a user of our Site or our Service. + +By accessing our Site or our Service, you accept our Privacy Policy and +[Terms of Use](website-terms-of-use), and you +consent to our collection, storage, use and disclosure of your Personal +Information as described in this Privacy Policy. + +1. INFORMATION WE COLLECT + +We collect "Non-Personal Information" and "Personal Information." +**Non-Personal Information** includes information that cannot be used to +personally identify you, such as anonymous usage data, general +demographic information we may collect, referring/exit pages and URLs, +platform types, preferences you submit and preferences that are +generated based on the data you submit and number of clicks. **Personal +Information** includes your email and name which you submit to us through the +registration process at the Site. + +1.1. _Information collected via Technology_ + +To activate the Service you do not need to submit any Personal +Information other than your email address and name. To use the Service +thereafter, you do not need to submit further Personal +Information. +However, in an effort to improve the quality of the Service, we track +information provided to us by your browser or by our software +application when you view or use the Service, such as the website you +came from (known as the "referring URL"), the type of browser you use, +the device from which you connected to the Service, the time and date of +access, and other information that does not personally identify you. We +track this information using cookies, or small text files which include +an anonymous unique identifier. Cookies are sent to a user's browser +from our servers and are stored on the user's computer hard drive. +Sending a cookie to a user's browser enables us to collect Non-Personal +information about that user and keep a record of the user's preferences +when utilizing our services, both on an individual and aggregate basis. +For example, the Company may use cookies to collect the following +information: + +- how often you use our websites and services +- which content and features you use + +The Company may use both persistent and session cookies; persistent +cookies remain on your computer after you close your session and until +you delete them, while session cookies expire when you close your +browser. + +1.2. _Information you provide us by registering for an account_ + +In addition to the information provided automatically by your browser +when you visit the Site, to become a subscriber to the Service you will +need to create a personal profile. You can create a profile by +registering with the Service and entering your email address, and +creating a user name and a password. By registering, you are authorizing +us to collect, store and use your email address in accordance with this +Privacy Policy. + +1.3. _Children's Privacy_ + +The Site and the Service are not directed to anyone under the age of 13. +The Site does not knowingly collect or solicit information from anyone +under the age of 13, or allow anyone under the age of 13 to sign up for +the Service. In the event that we learn that we have gathered personal +information from anyone under the age of 13 without the consent of a +parent or guardian, we will delete that information as soon as possible. +If you believe we have collected such information, please contact us at +info@getzep.com. + +2. HOW WE USE AND SHARE INFORMATION + +_Personal Information:_ + +Except as otherwise stated in this Privacy Policy, we do not sell, +trade, rent or otherwise share for marketing purposes your Personal +Information with third parties without your consent. We do share +Personal Information with vendors who are performing services for the +Company, such as the servers for our email communications who are +provided access to user's email address for purposes of sending emails +from us. Those vendors use your Personal Information only at our +direction and in accordance with our Privacy Policy. + +In general, the Personal Information you provide to us is used to help +us communicate with you. For example, we use Personal Information to +contact users in response to questions, solicit feedback from users, +provide technical support, and inform users about promotional offers. + +We may share Personal Information with outside parties if we have a +good-faith belief that access, use, preservation or disclosure of the +information is reasonably necessary to meet any applicable legal process +or enforceable governmental request; to enforce applicable Terms of +Service, including investigation of potential violations; address fraud, +security or technical concerns; or to protect against harm to the +rights, property, or safety of our users or the public as required or +permitted by law. + +_Non-Personal Information_ + +In general, we use Non-Personal Information to help us improve the +Service and customize the user experience. We also aggregate +Non-Personal Information in order to track trends and analyze use +patterns on the Site. This Privacy Policy does not limit in any way our +use or disclosure of Non-Personal Information and we reserve the right +to use and disclose such Non-Personal Information to our partners, +advertisers and other third parties at our discretion. + +In the event we undergo a business transaction such as a merger, +acquisition by another company, or sale of all or a portion of our +assets, your Personal Information may be among the assets transferred. +You acknowledge and consent that such transfers may occur and are +permitted by this Privacy Policy, and that any acquirer of our assets +may continue to process your Personal Information as set forth in this +Privacy Policy. If our information practices change at any time in the +future, we will post the policy changes to the Site so that you may opt +out of the new information practices. We suggest that you check the Site +periodically if you are concerned about how your information is used. + +3. HOW WE PROTECT INFORMATION + +We implement security measures designed to protect your information from +unauthorized access. Your account is protected by your account password +and we urge you to take steps to keep your personal information safe by +not disclosing your password and by logging out of your account after +each use. We further protect your information from potential security +breaches by implementing certain technological security measures +including encryption, firewalls and secure socket layer technology. +However, these measures do not guarantee that your information will not +be accessed, disclosed, altered or destroyed by breach of such firewalls +and secure server software. By using our Service, you acknowledge that +you understand and agree to assume these risks. + +4. YOUR RIGHTS REGARDING THE USE OF YOUR PERSONAL + INFORMATION + +You have the right at any time to prevent us from contacting you for +marketing purposes.  When we send a promotional communication to a user, +the user can opt out of further promotional communications by following +the unsubscribe instructions provided in each promotional e-mail. +Please note that notwithstanding the +promotional preferences you indicate by either unsubscribing, we may continue to +send you administrative emails including, for example, periodic updates +to our Privacy Policy. + +5. LINKS TO OTHER WEBSITES + +As part of the Service, we may provide links to or compatibility with +other websites or applications. However, we are not responsible for the +privacy practices employed by those websites or the information or +content they contain. This Privacy Policy applies solely to information +collected by us through the Site and the Service. Therefore, this +Privacy Policy does not apply to your use of a third party website +accessed by selecting a link on our Site or via our Service. To the +extent that you access or use the Service through or on another website +or application, then the privacy policy of that other website or +application will apply to your access or use of that site or +application. We encourage our users to read the privacy statements of +other websites before proceeding to use them. + +6. CHANGES TO OUR PRIVACY POLICY + +The Company reserves the right to change this policy and our Terms of +Service at any time.  We will notify you of significant changes to our +Privacy Policy by sending a notice to the primary email address +specified in your account or by placing a prominent notice on our site. +Significant changes will go into effect 30 days following such +notification. Non-material changes or clarifications will take effect +immediately. You should periodically check the Site and this privacy +page for updates. + +7. CONTACT US + +If you have any questions regarding this Privacy Policy or the practices +of this Site, please contact us by sending an email to info@getzep.com. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/terms-of-service.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/terms-of-service.mdx new file mode 100644 index 00000000000..6c594af5560 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/terms-of-service.mdx @@ -0,0 +1,112 @@ +--- +title: Terms of Service +slug: legal/terms-of-service +--- + + +**Version 1.0** + +**Last revised on: January 27^th^, 2024** + +If you signed a separate Cover Page to access the Product with the same +account, and that agreement has not ended, the terms below do not apply +to you. Instead, your separate Cover Page applies to your use of the +Product. + +This Agreement is between Zep Software, Inc. and the company or person +accessing or using the Product. This Agreement consists of: (1) the +Order Form and (2) the Key Terms, both of which are on the Cover Page +below, and (3) the Common Paper [Cloud Service Agreement Standard Terms +Version 1.1](https://commonpaper.com/standards/cloud-service-agreement/1.1/) +("Standard Terms"). Any modifications to the Standard Terms made in the +Cover Page will control over conflicts with the Standard Terms. +Capitalized words have the meanings or descriptions given in the Cover +Page or the Standard Terms. + +If you are accessing or using the Product on behalf of your company, you represent that you +are authorized to accept this Agreement ßon behalf of your company. By +signing up, accessing, or using the Product, Customer indicates its +acceptance of this Agreement and agrees to be bound by the terms and +conditions of this Agreement. + +Cover Page + +_Order Form_ + +**Cloud Service:** Zep is a cloud-based platform-as-a-service that +offers fast, scalable, privacy-compliant building blocks for Generative +AI apps. + +**Subscription Start Date:** The Effective Date + +**Subscription Period:** 1 month(s) + +**Non-Renewal Notice Period:** At least 30 days before the end of the +current Subscription Period. + +**Cloud Service Fees:** + +Section 5.2 of the Standard Terms is replaced with: Certain parts of the +Product have different pricing plans, which are available at Provider's +[pricing page](/service-plans). Within the Payment +Period, Customer will pay Provider fees based on the Product tier +selected at the time of account creation and Customer's usage per +Subscription Period. Provider may update Product pricing by giving at +least 30 days notice to Customer (including by email or notification +within the Product), and the change will apply in the next Subscription +Period. + +**Payment Period:** 5 day(s) from the last day of the Subscription +Period + +**Invoice Period:** Monthly + +_Key Terms_ + +**Customer:** The company or person who accesses or uses the Product. If +the person accepting this Agreement is doing so on behalf of a company, +all use of the word \"Customer\" in the Agreement will mean that +company. + +**Provider:** Zep Software, Inc. + +**Effective Date:** The date Customer first accepts this Agreement. + +**Covered Claims:** + +**Provider Covered Claims:** Any action, proceeding, or claim that the +Cloud Service, when used by Customer according to the terms of the +Agreement, violates, misappropriates, or otherwise infringes upon anyone +else's intellectual property or other proprietary rights. + +**Customer Covered Claims:** Any action, proceeding, or claim that (1) +the Customer Content, when used according to the terms of the Agreement, +violates, misappropriates, or otherwise infringes upon anyone else's +intellectual property or other proprietary rights; or (2) results from +Customer's breach or alleged breach of Section 2.1 (Restrictions on +Customer). + +**General Cap Amount:** + +The fees paid or payable by Customer to provider in the 12 month period +immediately before the claim + +**Governing Law:** The laws of the State of Delaware + +**Chosen Courts:** The state or federal courts located in Delaware + +**Notice Address:** + +For Provider: notices@getzep.com + +For Customer: The main email address on Customer's account + +_Changes to the Standard Terms_ + +**Publicity Rights:** Modifying Section 14.7 of the Standard Terms, +Provider may identify Customer and use Customer's logo and trademarks on +Provider's website and in marketing materials to identify Customer as a +user of the Product. Customer hereby grants Provider a non-exclusive, +royalty-free license to do so in connection with any marketing, +promotion, or advertising of Provider or the Product during the length +of the Agreement. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/website-terms-of-use.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/website-terms-of-use.mdx new file mode 100644 index 00000000000..87c88ec80b8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/legal/website-terms-of-use.mdx @@ -0,0 +1,648 @@ +--- +title: Website Terms of Use +slug: legal/website-terms-of-use +--- + + +**Version 1.0** + +**Last revised on: January 27^th^, 2024** + +The website located at getzep.com (the "**Site**") is a copyrighted work +belonging to Zep Software, Inc. ("**Company**", "**us**", "**our**", and +"**we**"). Certain features of the Site may be subject to additional +guidelines, terms, or rules, which will be posted on the Site in +connection with such features. All such additional terms, guidelines, +and rules are incorporated by reference into these Terms. + +These Terms of Use (these "**Terms**") set forth the legally binding +terms and conditions that govern your use of the Site. By accessing or +using the Site, you are accepting these Terms (on behalf of yourself or +the entity that you represent), and you represent and warrant that you +have the right, authority, and capacity to enter into these Terms (on +behalf of yourself or the entity that you represent). you may not access +or use the Site or accept the Terms if you are not at least 18 years +old. If you do not agree with all of the provisions of these Terms, do +not access and/or use the Site. + +**PLEASE BE AWARE THAT SECTION 8.2 CONTAINS PROVISIONS GOVERNING HOW TO +RESOLVE DISPUTES BETWEEN YOU AND COMPANY. AMONG OTHER THINGS, SECTION +8.2 INCLUDES AN AGREEMENT TO ARBITRATE WHICH REQUIRES, WITH LIMITED +EXCEPTIONS, THAT ALL DISPUTES BETWEEN YOU AND US SHALL BE RESOLVED BY +BINDING AND FINAL ARBITRATION. SECTION 8.2 ALSO CONTAINS A CLASS ACTION +AND JURY TRIAL WAIVER. PLEASE READ SECTION 8.2 CAREFULLY.** + +**UNLESS YOU OPT OUT OF THE AGREEMENT TO ARBITRATE WITHIN 30 DAYS: (1) +YOU WILL ONLY BE PERMITTED TO PURSUE DISPUTES OR CLAIMS AND SEEK RELIEF +AGAINST US ON AN INDIVIDUAL BASIS, NOT AS A PLAINTIFF OR CLASS MEMBER IN +ANY CLASS OR REPRESENTATIVE ACTION OR PROCEEDING AND YOU WAIVE YOUR +RIGHT TO PARTICIPATE IN A CLASS ACTION LAWSUIT OR CLASS-WIDE +ARBITRATION; AND (2) YOU ARE WAIVING YOUR RIGHT TO PURSUE DISPUTES OR +CLAIMS AND SEEK RELIEF IN A COURT OF LAW AND TO HAVE A JURY TRIAL.** + +1. **Accounts** + +1.1. **Account Creation.** In order to use certain features of the Site, +you must register for an account ("**Account**") and provide certain +information about yourself as prompted by the account registration +form. You represent and warrant that: (a) all required registration +information you submit is truthful and accurate; (b) you will +maintain the accuracy of such information. You may delete your +Account at any time, for any reason, by following the instructions +on the Site. Company may suspend or terminate your Account in +accordance with Section 7. + +1.2. **Account Responsibilities.** You are responsible for maintaining +the confidentiality of your Account login information and are fully +responsible for all activities that occur under your Account. You +agree to immediately notify Company of any unauthorized use, or +suspected unauthorized use of your Account or any other breach of +security. Company cannot and will not be liable for any loss or +damage arising from your failure to comply with the above +requirements. + +2. **Access to the Site** + +2.1. **License.** Subject to these Terms, Company grants you a +non-transferable, non-exclusive, revocable, limited license to use +and access the Site solely for your own personal, noncommercial use. + +2.2. **Certain Restrictions.** The rights granted to you in these Terms +are subject to the following restrictions: (a) you shall not +license, sell, rent, lease, transfer, assign, distribute, host, or +otherwise commercially exploit the Site, whether in whole or in +part, or any content displayed on the Site; (b) you shall not +modify, make derivative works of, disassemble, reverse compile or +reverse engineer any part of the Site; (c) you shall not access the +Site in order to build a similar or competitive website, product, or +service; and (d) except as expressly stated herein, no part of the +Site may be copied, reproduced, distributed, republished, +downloaded, displayed, posted or transmitted in any form or by any +means. Unless otherwise indicated, any future release, update, or +other addition to functionality of the Site shall be subject to +these Terms. All copyright and other proprietary notices on the Site +(or on any content displayed on the Site) must be retained on all +copies thereof. + +2.3. **Modification.** Company reserves the right, at any time, to +modify, suspend, or discontinue the Site (in whole or in part) with +or without notice to you. You agree that Company will not be liable +to you or to any third party for any modification, suspension, or +discontinuation of the Site or any part thereof. + +2.4. **No Support or Maintenance.** You acknowledge and agree that +Company will have no obligation to provide you with any support or +maintenance in connection with the Site. + +2.5. **Ownership.** You acknowledge that all the intellectual property +rights, including copyrights, patents, trade marks, and trade +secrets, in the Site and its content are owned by Company or +Company's suppliers. Neither these Terms (nor your access to the +Site) transfers to you or any third party any rights, title or +interest in or to such intellectual property rights, except for the +limited access rights expressly set forth in Section 2.1. Company +and its suppliers reserve all rights not granted in these Terms. +There are no implied licenses granted under these Terms. + +2.6. **Feedback.** If you provide Company with any feedback or +suggestions regarding the Site ("**Feedback**"), you hereby assign +to Company all rights in such Feedback and agree that Company shall +have the right to use and fully exploit such Feedback and related +information in any manner it deems appropriate. Company will treat +any Feedback you provide to Company as non-confidential and +non-proprietary. You agree that you will not submit to Company any +information or ideas that you consider to be confidential or +proprietary. + +3. **Indemnification.** You agree to indemnify and hold + Company (and its officers, employees, and agents) harmless, + including costs and attorneys' fees, from any claim or demand made + by any third party due to or arising out of (a) your use of the + Site, (b) your violation of these Terms or (c) your violation of + applicable laws or regulations. Company reserves the right, at your + expense, to assume the exclusive defense and control of any matter + for which you are required to indemnify us, and you agree to + cooperate with our defense of these claims. You agree not to settle + any matter without the prior written consent of Company. Company + will use reasonable efforts to notify you of any such claim, action + or proceeding upon becoming aware of it. + +4. **Third-Party Links & Ads; Other Users** + +4.1. **Third-Party Links & Ads.** The Site may contain links to +third-party websites and services, and/or display advertisements for +third parties (collectively, "**Third-Party Links & Ads**"). Such +Third-Party Links & Ads are not under the control of Company, and +Company is not responsible for any Third-Party Links & Ads. Company +provides access to these Third-Party Links & Ads only as a +convenience to you, and does not review, approve, monitor, endorse, +warrant, or make any representations with respect to Third-Party +Links & Ads. You use all Third-Party Links & Ads at your own risk, +and should apply a suitable level of caution and discretion in doing +so. When you click on any of the Third-Party Links & Ads, the +applicable third party's terms and policies apply, including the +third party's privacy and data gathering practices. You should make +whatever investigation you feel necessary or appropriate before +proceeding with any transaction in connection with such Third-Party +Links & Ads. + +4.2. **Other Users.** Your interactions with other Site users are solely +between you and such users. You agree that Company will not be +responsible for any loss or damage incurred as the result of any +such interactions. If there is a dispute between you and any Site +user, we are under no obligation to become involved. + +4.3.. **Release.** You hereby release and forever discharge Company (and +our officers, employees, agents, successors, and assigns) from, and +hereby waive and relinquish, each and every past, present and future +dispute, claim, controversy, demand, right, obligation, liability, +action and cause of action of every kind and nature (including +personal injuries, death, and property damage), that has arisen or +arises directly or indirectly out of, or that relates directly or +indirectly to, the Site (including any interactions with, or act or +omission of, other Site users or any Third-Party Links & Ads). IF +YOU ARE A CALIFORNIA RESIDENT, YOU HEREBY WAIVE CALIFORNIA CIVIL +CODE SECTION 1542 IN CONNECTION WITH THE FOREGOING, WHICH STATES: "A +GENERAL RELEASE DOES NOT EXTEND TO CLAIMS WHICH THE CREDITOR OR +RELEASING PARTY DOES NOT KNOW OR SUSPECT TO EXIST IN HIS OR HER +FAVOR AT THE TIME OF EXECUTING THE RELEASE, WHICH IF KNOWN BY HIM OR +HER MUST HAVE MATERIALLY AFFECTED HIS OR HER SETTLEMENT WITH THE +DEBTOR OR RELEASED PARTY." + +5. **Disclaimers** + +THE SITE IS PROVIDED ON AN "AS-IS" AND "AS AVAILABLE" BASIS, AND +COMPANY (AND OUR SUPPLIERS) EXPRESSLY DISCLAIM ANY AND ALL WARRANTIES +AND CONDITIONS OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, +INCLUDING ALL WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, TITLE, QUIET ENJOYMENT, ACCURACY, OR +NON-INFRINGEMENT. WE (AND OUR SUPPLIERS) MAKE NO WARRANTY THAT THE SITE +WILL MEET YOUR REQUIREMENTS, WILL BE AVAILABLE ON AN UNINTERRUPTED, +TIMELY, SECURE, OR ERROR-FREE BASIS, OR WILL BE ACCURATE, RELIABLE, FREE +OF VIRUSES OR OTHER HARMFUL CODE, COMPLETE, LEGAL, OR SAFE. IF +APPLICABLE LAW REQUIRES ANY WARRANTIES WITH RESPECT TO THE SITE, ALL +SUCH WARRANTIES ARE LIMITED IN DURATION TO 90 DAYS FROM THE DATE OF +FIRST USE. + +SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO +THE ABOVE EXCLUSION MAY NOT APPLY TO YOU. SOME JURISDICTIONS DO NOT +ALLOW LIMITATIONS ON HOW LONG AN IMPLIED WARRANTY LASTS, SO THE ABOVE +LIMITATION MAY NOT APPLY TO YOU. + +6. **Limitation on Liability** + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL COMPANY (OR +OUR SUPPLIERS) BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY LOST PROFITS, +LOST DATA, COSTS OF PROCUREMENT OF SUBSTITUTE PRODUCTS, OR ANY INDIRECT, +CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, SPECIAL OR PUNITIVE DAMAGES +ARISING FROM OR RELATING TO THESE TERMS OR YOUR USE OF, OR INABILITY TO +USE, THE SITE, EVEN IF COMPANY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. ACCESS TO, AND USE OF, THE SITE IS AT YOUR OWN DISCRETION +AND RISK, AND YOU WILL BE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR +DEVICE OR COMPUTER SYSTEM, OR LOSS OF DATA RESULTING THEREFROM. + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, NOTWITHSTANDING ANYTHING TO THE +CONTRARY CONTAINED HEREIN, OUR LIABILITY TO YOU FOR ANY DAMAGES ARISING +FROM OR RELATED TO THESE TERMS (FOR ANY CAUSE WHATSOEVER AND REGARDLESS +OF THE FORM OF THE ACTION), WILL AT ALL TIMES BE LIMITED TO A MAXIMUM OF +FIFTY US DOLLARS. THE EXISTENCE OF MORE THAN ONE CLAIM WILL NOT ENLARGE +THIS LIMIT. YOU AGREE THAT OUR SUPPLIERS WILL HAVE NO LIABILITY OF ANY +KIND ARISING FROM OR RELATING TO THESE TERMS. + +SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OR EXCLUSION OF LIABILITY +FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE LIMITATION OR +EXCLUSION MAY NOT APPLY TO YOU. + +7. **Term and Termination.** Subject to this Section, + these Terms will remain in full force and effect while you use the + Site. We may suspend or terminate your rights to use the Site + (including your Account) at any time for any reason at our sole + discretion, including for any use of the Site in violation of these + Terms. Upon termination of your rights under these Terms, your + Account and right to access and use the Site will terminate + immediately. Company will not have any liability whatsoever to you + for any termination of your rights under these Terms, including for + termination of your Account. Even after your rights under these + Terms are terminated, the following provisions of these Terms will + remain in effect: Sections 2.2 through 2.6 and Sections 3 through 8. + +8. **General** + +8.1. **Changes.** These Terms are subject to occasional revision, and if +we make any substantial changes, we may notify you by sending you an +e-mail to the last e-mail address you provided to us (if any), +and/or by prominently posting notice of the changes on our Site. You +are responsible for providing us with your most current e-mail +address. In the event that the last e-mail address that you have +provided us is not valid, or for any reason is not capable of +delivering to you the notice described above, our dispatch of the +e-mail containing such notice will nonetheless constitute effective +notice of the changes described in the notice. Continued use of our +Site following notice of such changes shall indicate your +acknowledgement of such changes and agreement to be bound by the +terms and conditions of such changes. + +8.2. **Dispute Resolution.** Please read the following +arbitration agreement in this Section (the "**Arbitration +Agreement**") carefully.  It requires you to arbitrate disputes with +Company, its parent companies, subsidiaries, affiliates, successors +and assigns and all of their respective officers, directors, +employees, agents, and representatives (collectively, the "**Company +Parties**") and limits the manner in which you can seek relief from +the Company Parties + +(a) **Applicability of Arbitration Agreement** You +agree that any dispute between you and any of the Company Parties +relating in any way to the Site, the services offered on the Site +(the "**Services**") or these Terms will be resolved by binding +arbitration, rather than in court, except that (1) you and the +Company Parties may assert individualized claims in small claims +court if the claims qualify, remain in such court and advance solely +on an individual, non-class basis; and (2) you or the Company +Parties may seek equitable relief in court for infringement or other +misuse of intellectual property rights (such as trademarks, trade +dress, domain names, trade secrets, copyrights, and patents). **This +Arbitration Agreement shall survive the expiration or termination of +these Terms and shall apply, without limitation, to all claims that +arose or were asserted before you agreed to these Terms (in +accordance with the preamble) or any prior version of these +Terms.** This Arbitration Agreement does not preclude you from +bringing issues to the attention of federal, state or local +agencies. Such agencies can, if the law allows, seek relief against +the Company Parties on your behalf. For purposes of this Arbitration +Agreement, "**Dispute**" will also include disputes that arose or +involve facts occurring before the existence of this or any prior +versions of the Agreement as well as claims that may arise after the +termination of these Terms. + +(b) **Informal Dispute Resolution.** There might be instances when a +Dispute arises between you and Company. If that occurs, Company is +committed to working with you to reach a reasonable resolution. You +and Company agree that good faith informal efforts to resolve +Disputes can result in a prompt, low‐cost and mutually beneficial +outcome. You and Company therefore agree that before either party +commences arbitration against the other (or initiates an action in +small claims court if a party so elects), we will personally meet +and confer telephonically or via videoconference, in a good faith +effort to resolve informally any Dispute covered by this Arbitration +Agreement ("**Informal Dispute Resolution Conference**"). If you are +represented by counsel, your counsel may participate in the +conference, but you will also participate in the conference. + +The party initiating a Dispute must give notice to the other party +in writing of its intent to initiate an Informal Dispute Resolution +Conference ("**Notice**"), which shall occur within 45 days after +the other party receives such Notice, unless an extension is +mutually agreed upon by the parties. Notice to Company that you +intend to initiate an Informal Dispute Resolution Conference should +be sent by email to: info@getzep.com, or by regular mail to 2261 Market Street #5686 +San Francisco, CA 94114. The Notice must include: (1) +your name, telephone number, mailing address, e‐mail address +associated with your account (if you have one); (2) the name, +telephone number, mailing address and e‐mail address of your +counsel, if any; and (3) a description of your Dispute. + +The Informal Dispute Resolution Conference shall be individualized +such that a separate conference must be held each time either party +initiates a Dispute, even if the same law firm or group of law firms +represents multiple users in similar cases, unless all parties +agree; multiple individuals initiating a Dispute cannot participate +in the same Informal Dispute Resolution Conference unless all +parties agree. In the time between a party receiving the Notice and +the Informal Dispute Resolution Conference, nothing in this +Arbitration Agreement shall prohibit the parties from engaging in +informal communications to resolve the initiating party's Dispute. +Engaging in the Informal Dispute Resolution Conference is a +condition precedent and requirement that must be fulfilled before +commencing arbitration. The statute of limitations and any filing +fee deadlines shall be tolled while the parties engage in the +Informal Dispute Resolution Conference process required by this +section. + +(c) **Arbitration Rules and Forum.** These Terms evidence +a transaction involving interstate commerce; and notwithstanding any +other provision herein with respect to the applicable substantive +law, the Federal Arbitration Act, 9 U.S.C. § 1 et seq., will govern +the interpretation and enforcement of this Arbitration Agreement and +any arbitration proceedings. If the Informal Dispute Resolution +Process described above does not resolve satisfactorily within 60 +days after receipt of your Notice, you and Company agree that either +party shall have the right to finally resolve the Dispute through +binding arbitration. The Federal Arbitration Act governs the +interpretation and enforcement of this Arbitration Agreement. The +arbitration will be conducted by JAMS, an established alternative +dispute resolution provider. Disputes involving claims and +counterclaims with an amount in controversy under \$250,000, not +inclusive of attorneys' fees and interest, shall be subject to JAMS' +most current version of the Streamlined Arbitration Rules and +procedures available +at http://www.jamsadr.com/rules-streamlined-arbitration/; all +other claims shall be subject to JAMS's most current version of the +Comprehensive Arbitration Rules and Procedures, available +at http://www.jamsadr.com/rules-comprehensive-arbitration/. JAMS's +rules are also available at www.jamsadr.com or by calling JAMS at +800-352-5267. A party who wishes to initiate arbitration must +provide the other party with a request for arbitration (the +"**Request**"). The Request must include: (1) the name, telephone +number, mailing address, e‐mail address of the party seeking +arbitration and the account username (if applicable) as well as the +email address associated with any applicable account; (2) a +statement of the legal claims being asserted and the factual bases +of those claims; (3) a description of the remedy sought and an +accurate, good‐faith calculation of the amount in controversy in +United States Dollars; (4) a statement certifying completion of the +Informal Dispute Resolution process as described above; and (5) +evidence that the requesting party has paid any necessary filing +fees in connection with such arbitration. + +If the party requesting arbitration is represented by counsel, the +Request shall also include counsel's name, telephone number, mailing +address, and email address. Such counsel must also sign the Request. +By signing the Request, counsel certifies to the best of counsel's +knowledge, information, and belief, formed after an inquiry +reasonable under the circumstances, that: (1) the Request is not +being presented for any improper purpose, such as to harass, cause +unnecessary delay, or needlessly increase the cost of dispute +resolution; (2) the claims, defenses and other legal contentions are +warranted by existing law or by a nonfrivolous argument for +extending, modifying, or reversing existing law or for establishing +new law; and (3) the factual and damages contentions have +evidentiary support or, if specifically so identified, will likely +have evidentiary support after a reasonable opportunity for further +investigation or discovery. + +Unless you and Company otherwise agree, or the Batch Arbitration +process discussed in Subsection 8.2(h) is triggered, the arbitration +will be conducted in the county where you reside. Subject to the +JAMS Rules, the arbitrator may direct a limited and reasonable +exchange of information between the parties, consistent with the +expedited nature of the arbitration. If the JAMS is not available to +arbitrate, the parties will select an alternative arbitral forum. +Your responsibility to pay any JAMS fees and costs will be solely as +set forth in the applicable JAMS Rules. + +You and Company agree that all materials and documents exchanged +during the arbitration proceedings shall be kept confidential and +shall not be shared with anyone except the parties' attorneys, +accountants, or business advisors, and then subject to the condition +that they agree to keep all materials and documents exchanged during +the arbitration proceedings confidential. + +(d) **Authority of Arbitrator.** The arbitrator shall have +exclusive authority to resolve all disputes subject to arbitration +hereunder including, without limitation, any dispute related to the +interpretation, applicability, enforceability or formation of this +Arbitration Agreement or any portion of the Arbitration Agreement, +except for the following: (1) all Disputes arising out of or +relating to the subsection entitled "Waiver of Class or Other +Non-Individualized Relief," including any claim that all or part of +the subsection entitled "Waiver of Class or Other Non-Individualized +Relief" is unenforceable, illegal, void or voidable, or that such +subsection entitled "Waiver of Class or Other Non-Individualized +Relief" has been breached, shall be decided by a court of competent +jurisdiction and not by an arbitrator; (2) except as expressly +contemplated in the subsection entitled "Batch Arbitration," all +Disputes about the payment of arbitration fees shall be decided only +by a court of competent jurisdiction and not by an arbitrator; (3) +all Disputes about whether either party has satisfied any condition +precedent to arbitration shall be decided only by a court of +competent jurisdiction and not by an arbitrator; and (4) all +Disputes about which version of the Arbitration Agreement applies +shall be decided only by a court of competent jurisdiction and not +by an arbitrator. The arbitration proceeding will not be +consolidated with any other matters or joined with any other cases +or parties, except as expressly provided in the subsection entitled +"Batch Arbitration." The arbitrator shall have the authority to +grant motions dispositive of all or part of any claim or dispute. +The arbitrator shall have the authority to award monetary damages +and to grant any non-monetary remedy or relief available to an +individual party under applicable law, the arbitral forum's rules, +and these Terms (including the Arbitration Agreement). The +arbitrator shall issue a written award and statement of decision +describing the essential findings and conclusions on which any award +(or decision not to render an award) is based, including the +calculation of any damages awarded. The arbitrator shall follow the +applicable law. The award of the arbitrator is final and binding +upon you and us. Judgment on the arbitration award may be entered in +any court having jurisdiction. + +(e) **Waiver of Jury Trial.** EXCEPT AS SPECIFIED in +section 8.2(a) YOU AND THE COMPANY PARTIES HEREBY WAIVE ANY +CONSTITUTIONAL AND STATUTORY RIGHTS TO SUE IN COURT AND HAVE A TRIAL +IN FRONT OF A JUDGE OR A JURY. You and the Company Parties are +instead electing that all covered claims and disputes shall be +resolved exclusively by arbitration under this Arbitration +Agreement, except as specified in Section 8.2(a) above. An +arbitrator can award on an individual basis the same damages and +relief as a court and must follow these Terms as a court would. +However, there is no judge or jury in arbitration, and court review +of an arbitration award is subject to very limited review. + +(f) **Waiver of Class or Other Non-Individualized +Relief.**  YOU AND COMPANY AGREE THAT, EXCEPT AS +SPECIFIED IN SUBSECTION 8.2(h) EACH OF US MAY BRING CLAIMS AGAINST +THE OTHER ONLY ON AN INDIVIDUAL BASIS AND NOT ON A CLASS, +REPRESENTATIVE, OR COLLECTIVE BASIS, AND THE PARTIES HEREBY WAIVE +ALL RIGHTS TO HAVE ANY DISPUTE BE BROUGHT, HEARD, ADMINISTERED, +RESOLVED, OR ARBITRATED ON A CLASS, COLLECTIVE, REPRESENTATIVE, OR +MASS ACTION BASIS. ONLY INDIVIDUAL RELIEF IS AVAILABLE, AND DISPUTES +OF MORE THAN ONE CUSTOMER OR USER CANNOT BE ARBITRATED OR +CONSOLIDATED WITH THOSE OF ANY OTHER CUSTOMER OR USER. Subject to +this Arbitration Agreement, the arbitrator may award declaratory or +injunctive relief only in favor of the individual party seeking +relief and only to the extent necessary to provide relief warranted +by the party's individual claim. Nothing in this paragraph is +intended to, nor shall it, affect the terms and conditions under the +Subsection 8.2(h) entitled "Batch Arbitration." Notwithstanding +anything to the contrary in this Arbitration Agreement, if a court +decides by means of a final decision, not subject to any further +appeal or recourse, that the limitations of this subsection, "Waiver +of Class or Other Non-Individualized Relief," are invalid or +unenforceable as to a particular claim or request for relief (such +as a request for public injunctive relief), you and Company agree +that that particular claim or request for relief (and only that +particular claim or request for relief) shall be severed from the +arbitration and may be litigated in the state or federal courts +located in the State of California. All other Disputes shall be +arbitrated or litigated in small claims court. This subsection does +not prevent you or Company from participating in a class-wide +settlement of claims. + +(g) **Attorneys' Fees and Costs.** The parties shall bear their own +attorneys' fees and costs in arbitration unless the arbitrator finds +that either the substance of the Dispute or the relief sought in the +Request was frivolous or was brought for an improper purpose (as +measured by the standards set forth in Federal Rule of Civil +Procedure 11(b)). If you or Company need to invoke the authority of +a court of competent jurisdiction to compel arbitration, then the +party that obtains an order compelling arbitration in such action +shall have the right to collect from the other party its reasonable +costs, necessary disbursements, and reasonable attorneys' fees +incurred in securing an order compelling arbitration. The prevailing +party in any court action relating to whether either party has +satisfied any condition precedent to arbitration, including the +Informal Dispute Resolution Process, is entitled to recover their +reasonable costs, necessary disbursements, and reasonable attorneys' +fees and costs. + +(h) **Batch Arbitration.** To increase the efficiency of administration +and resolution of arbitrations, you and Company agree that in the +event that there are 100 or more individual Requests of a +substantially similar nature filed against Company by or with the +assistance of the same law firm, group of law firms, or +organizations, within a 30 day period (or as soon as possible +thereafter), the JAMS shall (1) administer the arbitration demands +in batches of 100 Requests per batch (plus, to the extent there are +less than 100 Requests left over after the batching described above, +a final batch consisting of the remaining Requests); (2) appoint one +arbitrator for each batch; and (3) provide for the resolution of +each batch as a single consolidated arbitration with one set of +filing and administrative fees due per side per batch, one +procedural calendar, one hearing (if any) in a place to be +determined by the arbitrator, and one final award ("**Batch +Arbitration**"). + +All parties agree that Requests are of a "substantially similar +nature" if they arise out of or relate to the same event or factual +scenario and raise the same or similar legal issues and seek the +same or similar relief. To the extent the parties disagree on the +application of the Batch Arbitration process, the disagreeing party +shall advise the JAMS, and the JAMS shall appoint a sole standing +arbitrator to determine the applicability of the Batch Arbitration +process ("**Administrative Arbitrator**"). In an effort to expedite +resolution of any such dispute by the Administrative Arbitrator, the +parties agree the Administrative Arbitrator may set forth such +procedures as are necessary to resolve any disputes promptly. The +Administrative Arbitrator's fees shall be paid by Company. + +You and Company agree to cooperate in good faith with the JAMS to +implement the Batch Arbitration process including the payment of +single filing and administrative fees for batches of Requests, as +well as any steps to minimize the time and costs of arbitration, +which may include: (1) the appointment of a discovery special master +to assist the arbitrator in the resolution of discovery disputes; +and (2) the adoption of an expedited calendar of the arbitration +proceedings. + +This Batch Arbitration provision shall in no way be interpreted as +authorizing a class, collective and/or mass arbitration or action of +any kind, or arbitration involving joint or consolidated claims +under any circumstances, except as expressly set forth in this +provision. + +(i) **30-Day Right to Opt Out.**  You have the right to opt out of the +provisions of this Arbitration Agreement by sending a timely written +notice of your decision to opt out to the following address: +2261 Market Street #5686, San Francisco, CA 94114, or email to info@getzep.com, +within 30 days after first becoming subject to this Arbitration +Agreement. Your notice must include your name and address and a +clear statement that you want to opt out of this Arbitration +Agreement. If you opt out of this Arbitration Agreement, all other +parts of these Terms will continue to apply to you. Opting out of +this Arbitration Agreement has no effect on any other arbitration +agreements that you may currently have with us, or may enter into in +the future with us. + +(j) **Invalidity, Expiration.** Except as provided in the subsection +entitled "Waiver of Class or Other Non-Individualized Relief", if +any part or parts of this Arbitration Agreement are found under the +law to be invalid or unenforceable, then such specific part or parts +shall be of no force and effect and shall be severed and the +remainder of the Arbitration Agreement shall continue in full force +and effect. You further agree that any Dispute that you have with +Company as detailed in this Arbitration Agreement must be initiated +via arbitration within the applicable statute of limitation for that +claim or controversy, or it will be forever time barred. Likewise, +you agree that all applicable statutes of limitation will apply to +such arbitration in the same manner as those statutes of limitation +would apply in the applicable court of competent jurisdiction. + +(k)**Modification.** Notwithstanding any provision in +these Terms to the contrary, we agree that if Company makes any +future material change to this Arbitration Agreement, you may reject +that change within 30 days of such change becoming effective by +writing Company at the following address: 2261 Market Street #5686, +San Francisco, CA 94114, or email to info@getzep.com. Unless you reject the +change within 30 days of such change becoming effective by writing +to Company in accordance with the foregoing, your continued use of +the Site and/or Services, including the acceptance of products and +services offered on the Site following the posting of changes to +this Arbitration Agreement constitutes your acceptance of any such +changes. Changes to this Arbitration Agreement do not provide you +with a new opportunity to opt out of the Arbitration Agreement if +you have previously agreed to a version of these Terms and did not +validly opt out of arbitration. If you reject any change or update +to this Arbitration Agreement, and you were bound by an existing +agreement to arbitrate Disputes arising out of or relating in any +way to your access to or use of the Services or of the Site, any +communications you receive, any products sold or distributed through +the Site, the Services, or these Terms, the provisions of this +Arbitration Agreement as of the date you first accepted these Terms +(or accepted any subsequent changes to these Terms) remain in full +force and effect. Company will continue to honor any valid opt outs +of the Arbitration Agreement that you made to a prior version of +these Terms. + +8.3. **Export.** The Site may be subject to U.S. export control laws and +may be subject to export or import regulations in other countries. +You agree not to export, reexport, or transfer, directly or +indirectly, any U.S. technical data acquired from Company, or any +products utilizing such data, in violation of the United States +export laws or regulations. + +8.4. **Disclosures.** Company is located at the address in Section 8.8. +If you are a California resident, you may report complaints to the +Complaint Assistance Unit of the Division of Consumer Product of the +California Department of Consumer Affairs by contacting them in +writing at 400 R Street, Sacramento, CA 95814, or by telephone +at (800) 952-5210. + +9.5. **Electronic Communications.** The communications between you and +Company use electronic means, whether you use the Site or send us +emails, or whether Company posts notices on the Site or communicates +with you via email. For contractual purposes, you (a) consent to +receive communications from Company in an electronic form; and (b) +agree that all terms and conditions, agreements, notices, +disclosures, and other communications that Company provides to you +electronically satisfy any legal requirement that such +communications would satisfy if it were be in a hardcopy writing. +The foregoing does not affect your non-waivable rights. + +8.6. **Entire Terms.** These Terms constitute the entire agreement +between you and us regarding the use of the Site. Our failure to +exercise or enforce any right or provision of these Terms shall not +operate as a waiver of such right or provision. The section titles +in these Terms are for convenience only and have no legal or +contractual effect. The word "including" means "including without +limitation". If any provision of these Terms is, for any reason, +held to be invalid or unenforceable, the other provisions of these +Terms will be unimpaired and the invalid or unenforceable provision +will be deemed modified so that it is valid and enforceable to the +maximum extent permitted by law. Your relationship to Company is +that of an independent contractor, and neither party is an agent or +partner of the other. These Terms, and your rights and obligations +herein, may not be assigned, subcontracted, delegated, or otherwise +transferred by you without Company's prior written consent, and any +attempted assignment, subcontract, delegation, or transfer in +violation of the foregoing will be null and void. Company may freely +assign these Terms. The terms and conditions set forth in these +Terms shall be binding upon assignees. + +8.7. **Copyright/Trademark Information**. Copyright ©2024 Zep Software, Inc. All rights +reserved. All trademarks, logos and service marks ("**Marks**") +displayed on the Site are our property or the property of other +third parties. You are not permitted to use these Marks without our +prior written consent or the consent of such third party which may +own the Marks. + +**Contact Information:** + +Daniel Chalef + +Address: + +2261 Market Street + +#5686 + +San Francisco, CA 94114 diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/favicon.png b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/favicon.png new file mode 100644 index 00000000000..b79387e7a88 Binary files /dev/null and b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/favicon.png differ diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/zep-name-logo-gradient.svg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/zep-name-logo-gradient.svg new file mode 100644 index 00000000000..f8f57af1c7e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/zep-name-logo-gradient.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/zep-name-logo-pink.svg b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/zep-name-logo-pink.svg new file mode 100644 index 00000000000..3c5614c3645 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/logo/zep-name-logo-pink.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/projects.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/projects.mdx new file mode 100644 index 00000000000..f5713086881 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/projects.mdx @@ -0,0 +1,43 @@ +--- +slug: projects +--- + + + Projects encapsulate abstractions such as Users, Sessions, Memories, and Documents, and various settings. This allows + you to keep your data organized by service, environment (such as development or production), or any other criteria + that makes sense for your use case. + + +## Creating a Project + +When you sign up for Zep, your first project is automatically created. You'll be asked to configure a few project-specific settings (details below). If you need more projects, you can create them anytime through the [Zep Web App](https://app.getzep.com/projects/create). + + + Create a new project + + +### Project Essentials + +- Unique Project Name: Choose a unique name for your project. +- Description (Optional): Feel free to add a brief description of your project. +- Model Selection: Let us know which LLM you'll be using. We count tokens in Zep artifacts like Messages and Summaries to help you stay within your prompt token budget. Knowing your model choice ensures we use the correct tokenizer. + +> **You can modify your project settings later from the Dashboard.** + +## Message Window + +The Message Window, also known as the Memory Window, is a project setting that specifies how many messages are retrieved in a `GetMemory` call. + +It's set to a default of `6` messages and, while not adjustable during the initial project setup, can be easily changed later through the dashboard. + +## LLM Model Selection and Token Counting + +Select the LLM you'll be using for your Assistant. We count tokens in Zep artifacts like Messages and Summaries to help you stay within your prompt token budget. + +Knowing your model choice ensures we use the correct tokenizer. + +Available options are `GPT 3.5 or 4 family` and `Llama2 and related`. + +## API Keys + +API keys are specific to each project. You can create multiple keys for a single project. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/question-synthesis.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/question-synthesis.mdx new file mode 100644 index 00000000000..600a0628570 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/question-synthesis.mdx @@ -0,0 +1,58 @@ +--- +title: Question Synthesis +slug: question-synthesis +--- + + +Users often respond to questions with single-word answers that don't offer much information for search. +For example, a user may respond to a question about their favorite book with "Dune" or a question about their +dietary restrictions with "dairy". + + + Without context, many user messages lack information necessary to successfully embed a search query, resulting in poor + or irrelevant search results. + + +Zep provides a low-latency question synthesis API that can be used to generate a question from the current conversation context, using +the most recent message to center the question. The resulting question can be used to search over document Collections or +chat history Messages and Summaries. + +Zep's [Perpetual Memory](/chat-history-memory) uses this question synthesis functionality internally to ensure +that Memories it returns are relevant and useful. + +While it's possible to synthesize a question using a general purpose LLM, this is often a slow and inaccurate exercise. +Zep's private, fine-tuned models are designed to return results in hundreds of milliseconds. + + + Want to see Question Synthesis in action? Take a look at [Zep's LangService VectorStore + example](https://github.com/getzep/zep-python/blob/main/examples/langchain-langserve/app/message_history_vector_store_chain.py). + + + + + + +```Python +question = zep.memory.synthesize_question(session_id) +``` + + + + +```Typescript +const question = await zepClient.memory.synthesizeQuestion(sessionId); +``` + + + + +```Text +assistant: Iceland can be expensive. Costs depend on factors like accommodations, activities, and dining preferences. However, you can expect to spend around $200-$300 per day, not including flights. +user: Is it easy to find vegetarian or vegan food in Iceland? +assistant: Yes, Reykjavik has several vegetarian and vegan-friendly restaurants. Do you have any dietary restrictions? +user: Yes, dairy. +``` + +```Text +Can the user eat dairy products? +``` diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/sdks.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/sdks.mdx new file mode 100644 index 00000000000..4cc186fdf5f --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/sdks.mdx @@ -0,0 +1,146 @@ +--- +title: SDK Installation +subtitle: Zep provides SDKs for Python and TypeScript. +slug: sdks +--- + + +### Python + +To install the Zep Python SDK with Zep Cloud support, you'll need to install a release candidate version. + + + + + +```Bash +pip install --pre zep-python +``` + + + + +```Bash +poetry add zep-python@^2.0.0rc +``` + + + + +### TypeScript + +To install the Zep JavaScript SDK with Zep Cloud support, please install a pre-release version tagged with `@next`. + + + + + +```Bash +npm install @getzep/zep-js@next +``` + + + + +```Bash +yarn add @getzep/zep-js@next +``` + + + + +```Bash +pnpm install @getzep/zep-js@next +``` + + + + +## Initialize Client + + + + +```python +import os +from zep_python import ZepClient + +API_KEY = os.environ.get('ZEP_API_KEY') + +zep = ZepClient(api_key=API_KEY) +``` + + + + +```typescript +import { ZepClient } from "@getzep/zep-js"; + +API_KEY = process.env.ZEP_API_KEY; + +const zep = await ZepClient.init(API_KEY); +``` + + + + + + +**API Keys are project-specific.** You can generate a new API key from [Project Settings](projects). + + + **The Python SDK Supports Async Use** + +All methods are available as both sync and async, with the async methods prefixed with `a`. + + For example, zep-python + +has both `zep_client.memory.add_memory` and `zep_client.memory.aadd_memory` methods. + + + +## LangChain + + + + +The pre-release version of the `zep-python` SDK includes `ZepChatMessageHistory` and `ZepVectorStore` classes. + +These are designed to work seamlessly with [LangChain's Python Expression Language](https://python.langchain.com/docs/expression_language/). + +> To integrate these classes into your application, ensure the `langchain_core` package is installed. For installation guidance, please consult the [LangChain documentation](https://python.langchain.com/docs/get_started/installation#langchain-core). + +Import the classes as shown below: + +```python +from zep_python.langchain import ZepChatMessageHistory, ZepVectorStore +``` + + + + +The pre-release version of the `zep-js` SDK includes `ZepChatMessageHistory` and `ZepVectorStore` classes + +These are designed to work seamlessly with [LangChain's JavaScript Expression Language](https://js.langchain.com/docs/expression_language/). + +To utilize these classes in your application, ensure that the `langchain` package is installed: + +```bash +npm install langchain +``` + +To import these classes, use the following syntax: + +```typescript +import { ZepChatMessageHistory, ZepVectorStore } from "@getzep/zep-js/langchain"; +``` + + + + + + + +## LlamaIndex + +Stay tuned! We are in the process of updating our LlamaIndex integration to be compatible with the latest Zep API. diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/users.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/users.mdx new file mode 100644 index 00000000000..8d8a2fac6b8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/users.mdx @@ -0,0 +1,233 @@ +--- +slug: users +--- + +A User represents an individual interacting with your application. Each User can have multiple Sessions associated +with them, allowing you to track and manage the interactions of a user over time. + +The unique identifier for each user is their `UserID`. This can be any string value - for example, it could be a +username, an email address, or a UUID. You can also store additional data related to the user in the `metadata` field. + +The User object and its associated Sessions provide a powerful way to manage and understand the behavior of +individuals using your application. By associating Sessions with Users, you can track the progression of +conversations and interactions over time, providing valuable context and history. + +In the following sections, you will learn how to manage Users and their associated Sessions. + + +**Users Enable Simple User Privacy Management** + +By associating a User with a Session, you can delete all Sessions and session artifacts associated with a User +with a single API call. + + + +## The User model + +You can associate rich business context with a User: + +- `user_id`: A unique identifier of the user that maps to your internal User ID. +- `email`: The user's email. +- `first_name`: The user's first name. +- `last_name`: The user's last name. +- `metadata`: Any additional data associated with the user. + +## Adding a User + +You can add a new user by providing the user details. + + + + + +```python +from zep_python import ZepClient +from zep_python.user import CreateUserRequest, UpdateUserRequest + +client = ZepClient(api_key=API_KEY) + +user_request = CreateUserRequest( + user_id=user_id, + email="user@example.com", + first_name="Jane", + last_name="Smith", + metadata={"foo": "bar"}, +) +new_user = client.user.add(user_request) +``` + + + + +```typescript +import { ZepClient, CreateUserRequest, UpdateUserRequest } from "@getzep/zep-js"; + +const client: ZepClient = await ZepClient.init(API_KEY); + +const user_request: CreateUserRequest = { + userId: user_id, + email: "user@example.com", + firstName: "Jane", + lastName: "Smith", + metadata: { foo: "bar" }, +}; +const new_user = await client.user.add(user_request); +``` + + + + +> Learn how to associate [Sessions with Users](chat-history-memory/sessions) + +## Getting a User + +You can retrieve a user by their ID. + + + + + +```python +user = client.user.get("user123") +``` + + + + +```typescript +const user = await client.user.get("user123"); +``` + + + + +## Updating a User + +You can update a user's details by providing the updated user details. + + + + + +```python +user_request = UpdateUserRequest( + user_id=user_id, + email="updated_user@example.com", + first_name="Jane", + last_name="Smith", + metadata={"foo": "updated_bar"}, +) + +updated_user = client.user.update(user_request) +``` + + + + +```typescript +const user_request: UpdateUserRequest = { + userId: user_id, + email: "updated_user@example.com", + firstName: "Jane", + lastName: "Smith", + metadata: { foo: "updated_bar" }, +}; + +const updated_user = await client.user.update(user_request); +``` + + + + +## Deleting a User + +You can delete a user by their ID. + + + + + +```python +client.user.delete("user123") +``` + + + + +```typescript +await client.user.delete("user123"); +``` + + + + +## Getting a User's Sessions + +You can retrieve all Sessions for a user by their ID. + + + + +```python +sessions = client.user.get_sessions("user123") +``` + + + + +```typescript +const sessions = await client.user.getSessions("user123"); +``` + + + + +## Listing Users + +You can list all users, with optional limit and cursor parameters for pagination. + + + + + +```python +# List the first 10 users +users = client.user.list(limit=10, cursor=0) +``` + + + + +```typescript +// List the first 10 users +const users = await client.user.list(10, 0); +``` + + + + +## Listing Users in Chunks + +You can retrieve users in chunks of a specified size. This is a generator function that yields each chunk of users as +they are retrieved. + + + + +```python +for users in client.user.list_chunked(chunkSize=100): + process(users) +``` + + + + +```typescript +for await (const chunk of client.user.listChunked(100)) { + // Process each chunk of users + await processChunk(chunk); +} +``` + + + diff --git a/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/working-with-search.mdx b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/working-with-search.mdx new file mode 100644 index 00000000000..1776d3f26d3 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/__test__/outputs/zep/fern/working-with-search.mdx @@ -0,0 +1,287 @@ +--- +title: Constructing Search Queries +slug: working-with-search +--- + + + + Zep's Collection and Memory search supports semantic similarity search and similarity search re-ranked by [Maximal + Marginal Relevance](#maximal-marginal-relevance-re-ranking). Both of these search types can be filtered by + JSONPath-based metadata filters. Memory search also supports querying by message or summary creation date. + + +## Simple, Text-based Semantic Queries + +The simplest form of search query is a text-based semantic simailrity query. No metadata filter is required, and the query is simply a string of text. +Zep will convert the query into an embedding and find semantically similar documents, chat history summaries, or chat messages. + +Below is an example search against a chat session using only search text. + + + + + +```python +from zep_python import ( + MemorySearchPayload, + ZepClient, +) +client = ZepClient(api_key=API_KEY) + +search_payload = MemorySearchPayload( + text=query, + search_scope="messages", + metadata={"where": {"jsonpath": '$[*] ? (@.bar == "foo")'}}, + ) + +search_results = await client.memory.search_memory(session_id, search_payload) +``` + + + + + +```typescript +import { ZepClient, MemorySearchPayload } from "@getzep/zep-js"; + +const zepClient = await ZepClient.init(process.env.ZEP_API_KEY); + +const searchPayload = new MemorySearchPayload({ + text: query, + search_scope: "messages", + metadata: { where: { jsonpath: '$[*] ? (@.bar == "foo")' } }, +}); + +const searchResults = await client.memory.searchMemory(sessionId, searchPayload); +``` + + + + +Read more about [chat message history search](chat-history-memory/search). + +## Maximal Marginal Relevance Re-Ranking + +Zep supports re-ranking search results using [Maximal Marginal Relevance (MMR)](#maximal-marginal-relevance-re-ranking) over both chat history memory and document collections. + +Maximal Marginal Relevance (MMR) helps balance relevance and diversity in results +returned during information retrieval, which is useful in vector similarity searches for Retrieval-Augmented Generation (RAG) type applications. + +A similarity search may return many highly ranked results that are very similar to each other. Since each subsequent result doesn't add much new information to a prompt, +adding these may not be very useful. + +MMR helps to reduce redundancy in the results by re-ranking the results to promote diversity, with very similar results +downranked in favor of different, but still relevant, results. + +### How Zep's MMR Re-Ranking Works + +When you run a search re-ranked by MMR, Zep retrieves double the number of results, K, you requested. The entire resultset is then reranked using MMR, and the top K results are returned. + +If you request fewer than 10 results, Zep will return 10 results, but still return to you the top K results you requested. + +Zep's MMR algorithm is SIMD-hardware accelerated on `amd64` archicture CPUs, ensuring that MMR adds little overhead to your search. + +

Constructing an MMR Re-Ranked Search Query

+ +#### MMR Search over Chat History + + + + + +```python +from zep_python import ( + MemorySearchPayload, + ZepClient, +) + +client = ZepClient(api_key=API_KEY) + +search_payload = MemorySearchPayload( + text=query, + search_scope="summary", # This could be messages or summary + search_type="mmr", + mmr_lambda=0.5, # tune diversity vs relevance +) + +search_results = client.memory.search_memory( + session_id, search_payload, limit=3 +) +``` + + + + + +```typescript +import { ZepClient, MemorySearchPayload } from "@getzep/zep-js"; + +const zepClient = await ZepClient.init(process.env.ZEP_API_KEY); + +const searchPayload = new MemorySearchPayload({ + text: query, + search_scope: "summary", // This could be messages or summary + search_type: "mmr", + mmrLambda: 0.5, // tune diversity vs relevance +}); + +const searchResults = await client.memory.searchMemory(sessionId, searchPayload, 3); +``` + + + + +#### MMR Search over Document Collections + + + + + +```python +# This assumes you've created a collection and added documents to it +docs = collection.search( + text=query, + search_type="mmr", + mmr_lambda=0.5, + limit=3, +) +``` + + + + + +```typescript +// This assumes you've created a collection and added documents to it +const docs = await collection.search( + { + text: query, + search_type: "mmr", + mmr_lambda: 0.5, + }, + 3, +); +``` + + + + +## Filtering using Metadata + +Zep supports filtering search queries by metadata. Metadata filters are [JSONPath queries](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html) augmented by a simple boolean logic overlay. + +JSONPath queries allow for building sophisticated filters that match on elements at any level of the JSON document. The boolean logic overlay allows for combining multiple JSONPath queries using `and` and `or` operators. + +### Useful resources for building and testing JSONPath queries + +- [JSONPath Syntax](https://goessner.net/articles/JsonPath/) +- [JSONPath Online Evaluator](https://jsonpath.com/) +- [JSONPath Expression Tester](https://jsonpath.curiousconcept.com/#). + +### Constructing a JSONPath Query Filter + +The simplest form of a metadata filter looks as follows: + +```json +{ + "where": { "jsonpath": "$[*] ? (@.foo == \"bar\")" } +} +``` + +If a metadata field `foo` is equal to the string `"bar"`, then the document or message will be returned in the search results. + +Executing the above query against a chat session looks as follows: + + + + +```python +search_payload = MemorySearchPayload( + text="Is Lauren Olamina a character in a book", + metadata={ + "where": { + "jsonpath": '$[*] ? (@.author == "Octavia Butler")' + } + } +) + +search_results = client.memory.search_memory(session_id, search_payload) +``` + + + + + +```typescript +const searchPayload = new MemorySearchPayload({ + text: "Is Lauren Olamina a character in a book", + metadata: { where: { jsonpath: '$[*] ? (@.author == "Octavia Butler")' } }, +}); + +const search_results = await client.memory.searchMemory(session_id, search_payload); +``` + + + + +### Combining multiple JSONPath filters using boolean logic + +Multiple JSONPath queries can be combined using boolean logic. The following example will return documents or messages where the `author` field is equal to `"Octavia Butler"` **and** the `title` field is equal to `"Parable of the Sower"`. + +```json +{ + "where": { + "and": [ + { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" }, + { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" } + ] + } +} +``` + +Similarly, the following example will return documents or messages where the `author` field is equal to `"Octavia Butler"` **or** the `title` field is equal to `"Parable of the Sower"`. + +```json +{ + "where": { + "or": [ + { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" }, + { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" } + ] + } +} +``` + +Filter logic can be combined to create arbitrarily complex filters. For example, the following filter will return documents or messages where: + +- the `author` field is equal to (`"Octavia Butler"` **and** the `title` field is equal to `"Parable of the Sower"`) +- **or** the `title` field is equal to `"Parable of the Talents"`. + +```json +{ + "where": { + "or": [ + { + "and": [ + { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" }, + { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" } + ] + }, + { "jsonpath": "$[*] ? (@.title == \"Parable of the Talents\")" } + ] + } +} +``` + +## Querying by Message Creation Date + +Memory search supports querying by message creation date. The following example will return documents or messages created between June 1, 2023 and June 31, 2023. + +Datetime strings must be in ISO 8601 format. + +```json +{ + "start_date": "2023-06-01", + "end_date": "2023-06-31" +} +``` diff --git a/packages/cli/docs-importers/mintlify/src/convertColors.ts b/packages/cli/docs-importers/mintlify/src/convertColors.ts new file mode 100644 index 00000000000..154731a1cdd --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/convertColors.ts @@ -0,0 +1,16 @@ +import { docsYml } from "@fern-api/configuration"; +import { MintJsonSchema } from "./mintlify"; + +export function convertColors(colors: MintJsonSchema["colors"]): docsYml.RawSchemas.ColorsConfiguration { + return { + accentPrimary: { + // TODO: verify that we want to use colors.dark/light as the primary accent color + dark: colors.dark ?? colors.primary, + light: colors.light ?? colors.primary + }, + background: { + dark: colors.background?.dark, + light: colors.background?.light + } + }; +} diff --git a/packages/cli/docs-importers/mintlify/src/convertLogo.ts b/packages/cli/docs-importers/mintlify/src/convertLogo.ts new file mode 100644 index 00000000000..b88e1d5499e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/convertLogo.ts @@ -0,0 +1,55 @@ +import { MintJsonSchema } from "./mintlify"; +import { docsYml } from "@fern-api/configuration"; +import { stripLeadingSlash } from "@fern-api/core-utils"; +import { FernDocsBuilder } from "@fern-api/docs-importer-commons"; +import { AbsoluteFilePath, dirname, join, RelativeFilePath } from "@fern-api/fs-utils"; + +const LOGO_DEFAULT_HEIGHT = 28; // mintlify's default height (1.75rem); + +export declare namespace convertLogo { + interface Args { + absolutePathToMintJson: AbsoluteFilePath; + logo: MintJsonSchema["logo"]; + builder: FernDocsBuilder; + } +} + +export function convertLogo({ + logo, + builder, + absolutePathToMintJson +}: convertLogo.Args): docsYml.RawSchemas.LogoConfiguration | undefined { + if (logo == null) { + return undefined; + } + + if (typeof logo === "string") { + const relativeFilepath = RelativeFilePath.of(stripLeadingSlash(logo)); + builder.addAsset({ + absoluteFilePathToAsset: join(dirname(absolutePathToMintJson), relativeFilepath), + relativeFilePathFromDocsYml: relativeFilepath + }); + return { + light: relativeFilepath, + dark: relativeFilepath, + height: LOGO_DEFAULT_HEIGHT + }; + } + + const relativeFilepathToLight = RelativeFilePath.of(stripLeadingSlash(logo.light)); + builder.addAsset({ + absoluteFilePathToAsset: join(dirname(absolutePathToMintJson), relativeFilepathToLight), + relativeFilePathFromDocsYml: relativeFilepathToLight + }); + const relativeFilepathToDark = RelativeFilePath.of(stripLeadingSlash(logo.dark)); + builder.addAsset({ + absoluteFilePathToAsset: join(dirname(absolutePathToMintJson), relativeFilepathToDark), + relativeFilePathFromDocsYml: relativeFilepathToDark + }); + return { + light: relativeFilepathToLight, + dark: relativeFilepathToDark, + href: logo.href, + height: LOGO_DEFAULT_HEIGHT + }; +} diff --git a/packages/cli/docs-importers/mintlify/src/convertMarkdown.ts b/packages/cli/docs-importers/mintlify/src/convertMarkdown.ts new file mode 100644 index 00000000000..2f3c00a03d5 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/convertMarkdown.ts @@ -0,0 +1,99 @@ +import grayMatter from "gray-matter"; +import { MintlifyFrontmatter } from "./mintlify"; +import { readFile } from "fs/promises"; +import { FernRegistry as CjsFdrSdk } from "@fern-fern/fdr-cjs-sdk"; +import { AbsoluteFilePath, dirname, join, RelativeFilePath, relativize } from "@fern-api/fs-utils"; +import { FernDocsBuilder } from "@fern-api/docs-importer-commons"; + +export declare namespace convertMarkdown { + interface Args { + absolutePathToMintJson: AbsoluteFilePath; + absoluteFilepathToMarkdown: AbsoluteFilePath; + relativeFilepathFromRoot: RelativeFilePath; + builder: FernDocsBuilder; + } + + interface Return { + mintlifyFrontmatter: MintlifyFrontmatter; + relativeFilepathFromRoot: RelativeFilePath; + frontmatter: CjsFdrSdk.docs.latest.Frontmatter; + sidebarTitle: string | undefined; + content: string; + } +} + +export async function convertMarkdown({ + absolutePathToMintJson, + absoluteFilepathToMarkdown, + relativeFilepathFromRoot, + builder +}: convertMarkdown.Args): Promise { + const text = await readFile(absoluteFilepathToMarkdown, "utf-8"); + const { data, content } = parseMintlifyFrontmatter(text); + const slug = relativeFilepathFromRoot.replace(/\.(md|mdx)$/, ""); + + const transformedContent = markReferencedAssets({ + absolutePathToMintJson, + absoluteFilepathToMarkdown, + content, + builder + }); + + return { + mintlifyFrontmatter: data, + relativeFilepathFromRoot, + sidebarTitle: data.sidebarTitle ?? data.title, + frontmatter: { + title: data.title, + subtitle: data.description, + layout: data.mode != null ? "reference" : undefined, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + image: data["og:image"] as any, + slug + }, + content: transformedContent + }; +} + +function parseMintlifyFrontmatter(mdContent: string): { data: MintlifyFrontmatter; content: string } { + const { data, content } = grayMatter(mdContent); + return { data: data as MintlifyFrontmatter, content }; +} + +function isExternalUrl(url: string): boolean { + return url.startsWith("http:") || url.startsWith("https:") || url.startsWith("mailto:") || url.startsWith("tel:"); +} + +function markReferencedAssets({ + absolutePathToMintJson, + absoluteFilepathToMarkdown, + content, + builder +}: { + absolutePathToMintJson: AbsoluteFilePath; + absoluteFilepathToMarkdown: AbsoluteFilePath; + content: string; + builder: FernDocsBuilder; +}): string { + const transformedContent = content.replaceAll( + /src=["']([^"']+)["']/g, + (original: string, srcPath: string): string => { + if (isExternalUrl(srcPath)) { + return original; + } + + if (srcPath.startsWith("/")) { + const relativePath = RelativeFilePath.of(srcPath.substring(1)); + const absoluteFilePathToAsset = join(dirname(absolutePathToMintJson), relativePath); + builder.addAsset({ + absoluteFilePathToAsset, + relativeFilePathFromDocsYml: relativePath + }); + return `src="${relativePath}"`; + } + + return original; + } + ); + return transformedContent; +} diff --git a/packages/cli/docs-importers/mintlify/src/convertNavigationItem.ts b/packages/cli/docs-importers/mintlify/src/convertNavigationItem.ts new file mode 100644 index 00000000000..a850f6bcfbb --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/convertNavigationItem.ts @@ -0,0 +1,60 @@ +import { docsYml } from "@fern-api/configuration"; +import { isNonNullish } from "@fern-api/core-utils"; +import { FernDocsBuilder } from "@fern-api/docs-importer-commons"; +import { AbsoluteFilePath, dirname, join, RelativeFilePath } from "@fern-api/fs-utils"; +import { TaskContext } from "@fern-api/task-context"; +import { convertMarkdown } from "./convertMarkdown"; +import { MintNavigationItem } from "./mintlify"; + +export declare namespace convertNavigationItem { + interface Args { + absolutePathToMintJson: AbsoluteFilePath; + builder: FernDocsBuilder; + context: TaskContext; + item: MintNavigationItem; + } +} + +export async function convertNavigationItem({ + absolutePathToMintJson, + item, + builder, + context +}: convertNavigationItem.Args): Promise { + const section: docsYml.RawSchemas.SectionConfiguration = { + section: item.group, + contents: ( + await Promise.all( + item.pages.map(async (item): Promise => { + if (typeof item === "string") { + const relativeFilepathFromRoot = RelativeFilePath.of( + item.endsWith("mdx") ? item : `${item}.mdx` + ); + const convertedMarkdown = await convertMarkdown({ + absolutePathToMintJson, + relativeFilepathFromRoot, + absoluteFilepathToMarkdown: join(dirname(absolutePathToMintJson), relativeFilepathFromRoot), + builder + }); + if (convertedMarkdown.mintlifyFrontmatter.openapi != null) { + return undefined; + } + + builder.addMarkdownPage({ + frontmatter: convertedMarkdown.frontmatter, + markdown: convertedMarkdown.content, + relativeFilePathFromDocsYml: relativeFilepathFromRoot + }); + return { + page: convertedMarkdown.sidebarTitle ?? "", + path: relativeFilepathFromRoot + }; + } else { + return await convertNavigationItem({ absolutePathToMintJson, item, builder, context }); + } + }) + ) + ).filter(isNonNullish) + }; + return section; +} diff --git a/packages/cli/docs-importers/mintlify/src/index.ts b/packages/cli/docs-importers/mintlify/src/index.ts new file mode 100644 index 00000000000..9480b4fce6e --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/index.ts @@ -0,0 +1 @@ +export { MintlifyImporter } from "./MintlifyImporter"; diff --git a/packages/cli/docs-importers/mintlify/src/mintlify.ts b/packages/cli/docs-importers/mintlify/src/mintlify.ts new file mode 100644 index 00000000000..64af33df617 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/mintlify.ts @@ -0,0 +1,299 @@ +// this file was manually generated on April 22, 2024 based on +// https://mintlify.com/schema.json + +export interface MintJsonSchema { + $schema?: string; + mintlify?: string; + name: string; + logo?: + | string + | { + light: string; + dark: string; + href?: string; + }; + /** + * A path pointing to the favicon file in your docs folder, including the file extension. We recommend using an .svg or .png file. The favicon will automatically be resized to the appropriate sizes + */ + favicon: string; + /** + * A string or an array of strings of absolute or relative urls pointing to your OpenAPI files + */ + openapi?: string | string[]; + api?: { + baseUrl?: string | string[]; + auth?: { + method?: "bearer" | "basic" | "key" | "cobo"; + name?: string; + inputPrefix?: string; + }; + playground?: { + mode?: "show" | "simple" | "hide"; + }; + request?: { + example?: { + showOptionalParams?: boolean; + }; + }; + maintainOrder?: boolean; + }; + modeToggle?: { + default?: "light" | "dark"; + isHidden?: boolean; + }; + versions?: [ + ( + | string + | { + name: string; + url: string; + } + ), + ...( + | string + | { + name: string; + url: string; + } + )[] + ]; + metadata?: Record; + /** + * The colors to use in your documentation. At the very least, you must define the primary color. For example: { "colors": { "primary": "#ff0000" } } + */ + colors: { + primary: string; + light?: string; + dark?: string; + background?: { + light?: string; + dark?: string; + }; + anchors?: + | string + | { + from: string; + via?: string; + to: string; + }; + }; + /** + * An object containing the configuration for a Call-to-Action button. The object can have { "type": "link" } (the default) if you define a url and a name. For links to your GitHub repo, use { "type": "github" } + */ + topbarCtaButton?: + | { + type?: "link"; + name: string; + url: string; + } + | { + type: "github"; + /** + * A link to your GitHub repository + */ + url: string; + }; + /** + * An object containing the configuration for a Call-to-Action button. The object can have { "type": "link" } (the default) if you define a url and a name. For links to your GitHub repo, use { "type": "github" } + */ + topbarLinks?: ( + | { + type?: "link"; + name: string; + url: string; + } + | { + type: "github"; + /** + * A link to your GitHub repository + */ + url: string; + } + )[]; + navigation: MintNavigationItem[]; + primaryTab?: { + name: string; + }; + topAnchor?: { + name: string; + icon?: string; + iconType?: + | "brands" + | "duotone" + | "light" + | "regular" + | "sharp-light" + | "sharp-regular" + | "sharp-solid" + | "sharp-thin" + | "solid" + | "thin"; + }; + anchors?: { + name: string; + url: string; + icon?: string; + iconType?: + | "brands" + | "duotone" + | "light" + | "regular" + | "sharp-light" + | "sharp-regular" + | "sharp-solid" + | "sharp-thin" + | "solid" + | "thin"; + color?: + | string + | { + from: string; + via?: string; + to: string; + }; + isDefaultHidden?: boolean; + version?: string; + }[]; + tabs?: { + name: string; + url: string; + version?: string; + isDefaultHidden?: boolean; + }[]; + /** + * An object in which each key is the name of a social media platform, and each value is the url to your profile. For example: { "twitter": "https://twitter.com/mintlify" } + */ + footerSocials?: + | { + type: string; + url: string; + }[] + | Record; + backgroundImage?: string; + feedback?: { + thumbsRating?: boolean; + suggestEdit?: boolean; + raiseIssue?: boolean; + }; + analytics?: { + amplitude?: { + apiKey: string; + }; + clearbit?: { + publicApiKey: string; + }; + fathom?: { + siteId: string; + }; + ga4?: { + measurementId: string; + }; + gtm?: { + tagId: string; + }; + heap?: { + appId: string; + }; + hotjar?: { + hjid: string; + hjsv: string; + }; + koala?: { + publicApiKey: string; + }; + logrocket?: { + appId: string; + }; + mixpanel?: { + projectToken: string; + }; + pirsch?: { + id: string; + }; + posthog?: { + apiKey: string; + apiHost?: string; + }; + plausible?: { + domain: string; + }; + }; + integrations?: { + intercom?: string; + frontchat?: string; + osano?: Record & string; + }; + isWhiteLabeled?: boolean; + search?: { + prompt?: string; + }; + redirects?: { + source: string; + destination: string; + }[]; + seo?: { + indexHiddenPages?: boolean; + }; +} + +export type MintNavigationItemPage = string | MintNavigationItem; + +export interface MintNavigationItem { + /** + * The label for this group in the navigation sidebar + */ + group: string; + icon?: string; + iconType?: + | "brands" + | "duotone" + | "light" + | "regular" + | "sharp-light" + | "sharp-regular" + | "sharp-solid" + | "sharp-thin" + | "solid" + | "thin"; + version?: string; + pages: [MintNavigationItemPage, ...MintNavigationItemPage[]]; +} + +export interface MintlifyFrontmatter { + title?: string; + sidebarTitle?: string; + description?: string; + api?: string; + openapi?: string; + icon?: string; + iconType?: + | "brands" + | "duotone" + | "light" + | "regular" + | "sharp-light" + | "sharp-regular" + | "sharp-solid" + | "sharp-thin" + | "solid" + | "thin"; + mode?: "wide" | "custom"; + url?: string; + + // SEO + "og:site_name"?: string; + "og:title"?: string; + "og:description"?: string; + "og:url"?: string; + "og:image"?: string; + "og:locale"?: string; + "og:logo"?: string; + "article:publisher"?: string; + "twitter:title"?: string; + "twitter:description"?: string; + "twitter:url"?: string; + "twitter:image"?: string; + "twitter:site"?: string; + "og:image:width"?: string; + "og:image:height": string; +} diff --git a/packages/cli/docs-importers/mintlify/src/utils/getTabForMintItem.ts b/packages/cli/docs-importers/mintlify/src/utils/getTabForMintItem.ts new file mode 100644 index 00000000000..3c311e0d4c8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/src/utils/getTabForMintItem.ts @@ -0,0 +1,38 @@ +import { MintNavigationItem } from "../mintlify"; + +export declare namespace getTabForMintItem { + interface Args { + mintItem: MintNavigationItem; + } +} + +export function getTabForMintItem({ mintItem }: getTabForMintItem.Args): string | undefined { + const firstPage = getFirstPage({ item: mintItem }); + if (firstPage == null) { + return undefined; + } + return getFirstUrlSegment({ path: firstPage }); +} + +function getFirstPage({ item }: { item: MintNavigationItem }): string | undefined { + for (const page of item.pages) { + if (typeof page === "string") { + return page; + } else { + const firstPage = getFirstPage({ item: page }); + if (firstPage != null) { + return firstPage; + } + } + } + return undefined; +} + +/** + * @returns the first segment of the url path for the page. So if the path is `/reference/guides/abc`, + * then this function will return `reference` + */ +function getFirstUrlSegment({ path }: { path: string }): string | undefined { + const segments = path.split("/"); + return segments[0]; +} diff --git a/packages/cli/docs-importers/mintlify/tsconfig.json b/packages/cli/docs-importers/mintlify/tsconfig.json new file mode 100644 index 00000000000..807fd43d5a8 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../../shared/tsconfig.shared.json", + "compilerOptions": { "composite": true, "outDir": "lib", "rootDir": "src" }, + "include": ["./src/**/*"], + "references": [ { "path": "../../../commons/core-utils" }, { "path": "../../../commons/fs-utils" }, { "path": "../commons" }, { "path": "../../task-context" }, { "path": "../../logger" }] +} diff --git a/packages/cli/docs-importers/mintlify/vitest.config.ts b/packages/cli/docs-importers/mintlify/vitest.config.ts new file mode 100644 index 00000000000..d11017dc676 --- /dev/null +++ b/packages/cli/docs-importers/mintlify/vitest.config.ts @@ -0,0 +1 @@ +export { default } from "../../../../shared/vitest.config"; diff --git a/packages/cli/docs-markdown-utils/src/index.ts b/packages/cli/docs-markdown-utils/src/index.ts index 685b8d33f12..4e7b1e6d7cb 100644 --- a/packages/cli/docs-markdown-utils/src/index.ts +++ b/packages/cli/docs-markdown-utils/src/index.ts @@ -1,3 +1,4 @@ export { parseImagePaths, replaceImagePathsAndUrls } from "./parseImagePaths"; export { replaceReferencedMarkdown } from "./replaceReferencedMarkdown"; export { replaceReferencedCode } from "./replaceReferencedCode"; +export { parseMarkdownToTree } from "./parseMarkdownToTree"; diff --git a/packages/cli/docs-markdown-utils/src/parseImagePaths.ts b/packages/cli/docs-markdown-utils/src/parseImagePaths.ts index cd7d0a30820..3df102387bc 100644 --- a/packages/cli/docs-markdown-utils/src/parseImagePaths.ts +++ b/packages/cli/docs-markdown-utils/src/parseImagePaths.ts @@ -6,6 +6,7 @@ import { fromMarkdown } from "mdast-util-from-markdown"; import { mdxFromMarkdown } from "mdast-util-mdx"; import { mdx } from "micromark-extension-mdx"; import { visit } from "unist-util-visit"; +import { parseMarkdownToTree } from "./parseMarkdownToTree"; interface AbsolutePathMetadata { absolutePathToMdx: AbsoluteFilePath; @@ -48,10 +49,7 @@ export function parseImagePaths( visitFrontmatterImages(data, ["image", "og:image", "og:logo", "twitter:image"], mapImage); - const tree = fromMarkdown(content, { - extensions: [mdx()], - mdastExtensions: [mdxFromMarkdown()] - }); + const tree = parseMarkdownToTree(content); let offset = 0; diff --git a/packages/cli/docs-markdown-utils/src/parseMarkdownToTree.ts b/packages/cli/docs-markdown-utils/src/parseMarkdownToTree.ts new file mode 100644 index 00000000000..dba21bcdd50 --- /dev/null +++ b/packages/cli/docs-markdown-utils/src/parseMarkdownToTree.ts @@ -0,0 +1,11 @@ +import { fromMarkdown } from "mdast-util-from-markdown"; +import { Root } from "mdast-util-from-markdown/lib"; +import { mdxFromMarkdown } from "mdast-util-mdx"; +import { mdx } from "micromark-extension-mdx"; + +export function parseMarkdownToTree(content: string): Root { + return fromMarkdown(content, { + extensions: [mdx()], + mdastExtensions: [mdxFromMarkdown()] + }); +} diff --git a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts index b1535a0bff4..d2e92cde965 100644 --- a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts +++ b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts @@ -7,7 +7,7 @@ import { replaceReferencedMarkdown } from "@fern-api/docs-markdown-utils"; import { APIV1Write, DocsV1Write, FernNavigation } from "@fern-api/fdr-sdk"; -import { AbsoluteFilePath, listFiles, relative, RelativeFilePath, resolve } from "@fern-api/fs-utils"; +import { AbsoluteFilePath, listFiles, relative, RelativeFilePath, relativize, resolve } from "@fern-api/fs-utils"; import { generateIntermediateRepresentation } from "@fern-api/ir-generator"; import { IntermediateRepresentation } from "@fern-api/ir-sdk"; import { TaskContext } from "@fern-api/task-context"; @@ -177,12 +177,12 @@ export class DocsDefinitionResolver { const stats = await stat(absoluteFilePath); if (stats.isDirectory()) { - const files = await listFiles(absoluteFilePath, "{js,ts,jsx,tsx}"); + const files = await listFiles(absoluteFilePath, "{js,ts,jsx,tsx,md,mdx}"); files.forEach((file) => { jsFilePaths.add(file); }); - } else if (absoluteFilePath.match(/\.(js|ts|jsx|tsx)$/) != null) { + } else if (absoluteFilePath.match(/\.(js|ts|jsx|tsx|md|mdx)$/) != null) { jsFilePaths.add(absoluteFilePath); } }) diff --git a/packages/cli/ete-tests/src/tests/generate/__snapshots__/generate.test.ts.snap b/packages/cli/ete-tests/src/tests/generate/__snapshots__/generate.test.ts.snap index 28eccaf7a75..2282c7ba388 100644 --- a/packages/cli/ete-tests/src/tests/generate/__snapshots__/generate.test.ts.snap +++ b/packages/cli/ete-tests/src/tests/generate/__snapshots__/generate.test.ts.snap @@ -2,6 +2,6 @@ exports[`fern generate > missing docs page 1`] = ` "[docs]: Found 1 errors and 0 warnings. Run fern check --warnings to print out the warnings. -[docs]: docs.yml -> navigation -> 0 -> page +[docs]: docs.yml -> navigation -> 0 -> path Path missing.md does not exist" `; diff --git a/packages/cli/openapi-ir-to-fern-tests/src/__test__/__snapshots__/switchboard.test.ts.snap b/packages/cli/openapi-ir-to-fern-tests/src/__test__/__snapshots__/switchboard.test.ts.snap new file mode 100644 index 00000000000..b861a41e458 --- /dev/null +++ b/packages/cli/openapi-ir-to-fern-tests/src/__test__/__snapshots__/switchboard.test.ts.snap @@ -0,0 +1,3883 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`open api parser > switchboard > docs 1`] = ` +{ + "definitionFiles": { + "auth.yml": { + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "andRedirect": { + "auth": false, + "display-name": "Auth and redirect sessions", + "docs": "Auth and redirect sessions by ID", + "examples": [ + {}, + ], + "method": "GET", + "pagination": undefined, + "path": "/auth", + "request": { + "headers": { + "x-call-type": { + "docs": "call type, e.g. CHROMEDRIVER_EXTERNAL", + "name": "callType", + "type": "optional", + }, + "x-session-id": { + "docs": "session id", + "name": "sessionId", + "type": "optional", + }, + }, + "name": "AuthAndRedirectRequest", + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + "extensions.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "delete": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Delete extensions matching name query", + "docs": "Delete an extension by ID", + "examples": [ + { + "query-parameters": { + "name": "Widget", + }, + }, + ], + "method": "DELETE", + "pagination": undefined, + "path": "/extensions", + "request": { + "name": "ExtensionsDeleteRequest", + "query-parameters": { + "name": { + "docs": "Name to search for, supports regex, case insensitive", + "type": "optional", + }, + }, + }, + }, + "get": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Get extensions", + "docs": "Get extensions, searching by name", + "examples": [ + { + "query-parameters": { + "name": "Widget", + }, + "response": { + "body": { + "data": [ + { + "date_created": "2024-01-15T09:30:00Z", + "id": "73df0106-1781-4b03-a288-e749e1a43481", + "name": "Special Widget", + "upload_uri": "upload_uri", + }, + ], + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/extensions", + "request": { + "name": "ExtensionsGetRequest", + "query-parameters": { + "name": { + "docs": "Name to search for, supports regex, case insensitive", + "type": "optional", + }, + }, + }, + "response": { + "docs": "OK", + "type": "root.ListExtensionV1EnvelopeDefaultMetaWrapper", + }, + }, + "getById": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Get an extension", + "docs": "Get an extension by ID", + "errors": [ + "root.ExtensionsGetByIdRequestNotFoundError", + "root.ExtensionsGetByIdRequestUnprocessableEntityError", + "root.ExtensionsGetByIdRequestInternalServerError", + ], + "examples": [ + { + "path-parameters": { + "id": "4a61a55c-391b-4f73-957e-ffbd29ac7cba", + }, + "response": { + "body": { + "data": { + "date_created": "2024-01-15T09:30:00Z", + "id": "73df0106-1781-4b03-a288-e749e1a43481", + "name": "Special Widget", + "upload_uri": "upload_uri", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/extensions/{id}", + "path-parameters": { + "id": { + "docs": "UUID of the profile to get", + "type": "string", + }, + }, + "response": { + "docs": "OK", + "type": "root.ExtensionV1EnvelopeDefaultMetaWrapper", + }, + }, + "post": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Create an extension", + "docs": undefined, + "examples": [ + { + "request": { + "name": "Special Widget", + }, + "response": { + "body": { + "data": { + "date_created": "2024-01-15T09:30:00Z", + "id": "73df0106-1781-4b03-a288-e749e1a43481", + "name": "Special Widget", + "upload_uri": "upload_uri", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/extensions", + "request": { + "body": { + "properties": { + "name": { + "docs": "Name of the extension", + "type": "string", + }, + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "ExtensionsPostRequest", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.ExtensionV1EnvelopeDefaultMetaWrapper", + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + "profiles.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "delete": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Delete profiles matching query", + "docs": "Delete profiles matching query", + "examples": [ + { + "query-parameters": { + "name": "^Acme.*", + }, + }, + ], + "method": "DELETE", + "pagination": undefined, + "path": "/profiles", + "request": { + "name": "ProfilesDeleteRequest", + "query-parameters": { + "name": { + "docs": "Name to search for, supports regex, case insensitive", + "type": "optional", + }, + "tags": { + "allow-multiple": true, + "docs": "a comma separated list of tags to filter by (joined by OR, e.g. tag is red OR blue)", + "type": "optional", + }, + }, + }, + }, + "get": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get profiles", + "docs": "Get profiles, searching by name or tags", + "examples": [ + { + "query-parameters": { + "name": "^Acme.*", + }, + "response": { + "body": { + "data": [ + { + "name": "Acme corp login", + "status": "status", + "tags": [ + "blue", + "red", + ], + "upload_uri": "upload_uri", + }, + ], + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/profiles", + "request": { + "name": "ProfilesGetRequest", + "query-parameters": { + "name": { + "docs": "Name to search for, supports regex, case insensitive", + "type": "optional", + }, + "tags": { + "allow-multiple": true, + "docs": "a comma separated list of tags to filter by (joined by OR, e.g. tag is red OR blue)", + "type": "optional", + }, + }, + }, + "response": { + "docs": "OK", + "type": "root.ListProfileV1EnvelopeDefaultMetaWrapper", + }, + }, + "getById": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get a profile", + "docs": "Get a profile by ID", + "errors": [ + "root.ProfilesGetByIdRequestNotFoundError", + "root.ProfilesGetByIdRequestUnprocessableEntityError", + "root.ProfilesGetByIdRequestInternalServerError", + ], + "examples": [ + { + "path-parameters": { + "id": "4a61a55c-391b-4f73-957e-ffbd29ac7cba", + }, + "response": { + "body": { + "data": { + "name": "Acme corp login", + "status": "status", + "tags": [ + "blue", + "red", + ], + "upload_uri": "upload_uri", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/profiles/{id}", + "path-parameters": { + "id": { + "docs": "name of the profile to get", + "type": "string", + }, + }, + "response": { + "docs": "OK", + "type": "root.ProfileV1EnvelopeDefaultMetaWrapper", + }, + }, + "post": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Create a profile", + "docs": undefined, + "examples": [ + { + "request": { + "name": "Acme corp login", + }, + "response": { + "body": { + "data": { + "name": "Acme corp login", + "status": "status", + "tags": [ + "blue", + "red", + ], + "upload_uri": "upload_uri", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/profiles", + "request": { + "body": { + "properties": { + "name": { + "docs": "Name of the Profile", + "type": "string", + "validation": { + "format": undefined, + "maxLength": 100, + "minLength": 1, + "pattern": undefined, + }, + }, + "tags": { + "docs": "a comma separated list of tags for this profile", + "type": "optional>", + }, + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "ProfileInput", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.ProfileV1EnvelopeDefaultMetaWrapper", + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + "sessions.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "events": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get a session event stream", + "docs": "Get a session event stream for a given session ID", + "errors": [ + "root.SessionsEventsRequestNotFoundError", + "root.SessionsEventsRequestUnprocessableEntityError", + "root.SessionsEventsRequestInternalServerError", + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions/{id}/events", + "path-parameters": { + "id": { + "docs": "UUID of the session to get status info for", + "type": "string", + }, + }, + "response-stream": { + "docs": "OK", + "format": "sse", + "type": "SessionsEventsResponse", + }, + }, + "getinfo": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get info for a session", + "docs": "Get a session by ID", + "errors": [ + "root.SessionsGetInfoRequestNotFoundError", + "root.SessionsGetInfoRequestUnprocessableEntityError", + "root.SessionsGetInfoRequestInternalServerError", + ], + "examples": [ + { + "path-parameters": { + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + }, + "response": { + "body": { + "data": { + "cdp_url": "cdp_url", + "cdp_ws_url": "cdp_ws_url", + "chromedriver_url": "chromedriver_url", + "configuration": { + "persist_profile": true, + "persist_profile_name": "default", + "persist_profile_tags": [ + "persist_profile_tags", + ], + "profile_id": "linkedin", + "timeoutMinutes": 10, + }, + "current_usage": 1000000, + "date_created": "2024-01-15T09:30:00Z", + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "last_activity": "2024-01-15T09:30:00Z", + "live_view": { + "airtop_browser_url": "airtop_browser_url", + "token": "token", + }, + "status": "active", + "url": "url", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions/{id}", + "path-parameters": { + "id": { + "docs": "UUID of the session to get", + "type": "string", + }, + }, + "response": { + "docs": "OK", + "type": "root.SessionWithConnectionInfoEnvelopeDefaultMetaWrapper", + }, + }, + "list": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get a list of sessions", + "docs": "Get a list of sessions by ID", + "errors": [ + "root.SessionsListRequestNotFoundError", + "root.SessionsListRequestUnprocessableEntityError", + "root.SessionsListRequestInternalServerError", + ], + "examples": [ + { + "query-parameters": { + "limit": 10, + "offset": 1, + }, + "response": { + "body": { + "data": { + "pagination": { + "currentLimit": 1000000, + "currentPage": 1000000, + "finalCount": 1000000, + "hasMore": true, + "initialCount": 1000000, + "nextOffset": 1000000, + "numberOfPages": 1000000, + "totalItems": 1000000, + }, + "sessions": [ + { + "configuration": { + "persist_profile": true, + "persist_profile_name": "default", + "profile_id": "linkedin", + "timeoutMinutes": 10, + }, + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "status": "active", + }, + ], + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions", + "request": { + "name": "SessionsListRequest", + "query-parameters": { + "limit": { + "docs": "limit for pagination", + "type": "optional", + }, + "offset": { + "docs": "offset for pagination", + "type": "optional", + }, + "sessionIds": { + "allow-multiple": true, + "docs": "a comma separated list of UUIDs of the session to get", + "type": "optional", + }, + "status": { + "docs": "status of the session to get", + "type": "optional", + }, + }, + }, + "response": { + "docs": "OK", + "type": "root.SessionsWithPaginationEnvelopeDefaultMetaWrapper", + }, + }, + "post": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Create a session", + "docs": undefined, + "examples": [ + { + "request": {}, + "response": { + "body": { + "data": { + "cdp_url": "cdp_url", + "cdp_ws_url": "cdp_ws_url", + "chromedriver_url": "chromedriver_url", + "configuration": { + "persist_profile": true, + "persist_profile_name": "default", + "persist_profile_tags": [ + "persist_profile_tags", + ], + "profile_id": "linkedin", + "timeoutMinutes": 10, + }, + "current_usage": 1000000, + "date_created": "2024-01-15T09:30:00Z", + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "last_activity": "2024-01-15T09:30:00Z", + "live_view": { + "airtop_browser_url": "airtop_browser_url", + "token": "token", + }, + "status": "active", + "url": "url", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/sessions", + "request": { + "body": { + "properties": { + "configuration": { + "docs": "Session configuration", + "type": "optional", + }, + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "SessionRestInputV1", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.SessionWithConnectionInfoEnvelopeDefaultMetaWrapper", + }, + }, + "terminate": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Ends a session", + "docs": "Ends a session by ID. If a given session id does not exist within the organization, it is ignored.", + "examples": [ + { + "path-parameters": { + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + }, + }, + ], + "method": "DELETE", + "pagination": undefined, + "path": "/sessions/{id}", + "path-parameters": { + "id": { + "docs": "UUID of the session to delete", + "type": "string", + }, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "types": { + "SessionsEventsResponse": { + "availability": undefined, + "base-properties": {}, + "discriminant": "event", + "docs": "Each oneOf object in the array represents one possible Server Sent Events (SSE) message, serialized as UTF-8 text according to the SSE specification.", + "encoding": undefined, + "source": { + "openapi": "switchboard/openapi.yml", + }, + "union": { + "error": { + "type": "SessionsEventsResponseError", + }, + "status": { + "type": "SessionsEventsResponseStatus", + }, + }, + }, + "SessionsEventsResponseError": { + "docs": undefined, + "properties": { + "data": "root.ErrorMessage", + "id": { + "docs": "The event ID.", + "type": "optional", + }, + "retry": { + "docs": "The retry time in milliseconds.", + "type": "optional", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionsEventsResponseStatus": { + "docs": undefined, + "properties": { + "data": "root.StatusMessage", + "id": { + "docs": "The event ID.", + "type": "optional", + }, + "retry": { + "docs": "The retry time in milliseconds.", + "type": "optional", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionsListRequestStatus": { + "docs": "status of the session to get", + "enum": [ + "awaiting_capacity", + "initializing", + "running", + "ended", + ], + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + }, + "windows.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "get-window-info": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get information about a browser window in a session", + "docs": undefined, + "examples": [ + { + "path-parameters": { + "sessionId": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "windowId": "7334da2a-91b0-42c5-6156-76a5eba87430", + }, + "query-parameters": { + "Screen resolution": "1920x1080", + }, + "response": { + "body": { + "data": { + "liveViewUrl": "liveViewUrl", + "token": "token", + "windowId": "windowId", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions/{sessionId}/windows/{windowId}/get-window-info", + "path-parameters": { + "sessionId": { + "docs": "UUID of the session that owns the window", + "type": "string", + }, + "windowId": { + "docs": "id of the browser window, either the windowId (uuid) or targetId (simple string)", + "type": "string", + }, + }, + "request": { + "name": "GetWindowInfoRequest", + "query-parameters": { + "Screen resolution": { + "docs": "Ex: Screen resolution of the live view in format wxh, e.g. 1920x1080. When this value is provided the live view won't adapt it's resolution to the live view window size", + "type": "optional", + }, + "disableResize": { + "docs": "By default browsers will adapt its resolution to the live view window size. This flag disables that behavior", + "type": "optional", + }, + "includeNavigationBar": { + "docs": "Renders a navigation bar as part of the live view session", + "type": "optional", + }, + }, + }, + "response": { + "docs": "Created", + "type": "root.WindowEnvelopeDefaultMetaWrapper", + }, + }, + "prompt-content": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Submit a prompt about the content in a specific browser window.", + "docs": undefined, + "examples": [ + { + "path-parameters": { + "sessionId": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "windowId": "0334da2a-91b0-42c5-6156-76a5eba87430", + }, + "request": { + "prompt": "What is the main idea of this page?", + }, + "response": { + "body": { + "data": { + "modelResponse": "modelResponse", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "clientProvided": { + "clientRequestId": "clientRequestId", + "timeThresholdMs": 1000000, + }, + "requestId": "requestId", + "status": "success", + "usage": { + "credits": 1000000, + "id": "id", + }, + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/sessions/{sessionId}/windows/{windowId}/prompt-content", + "path-parameters": { + "sessionId": { + "docs": "The session id to submit a prompt about", + "type": "string", + }, + "windowId": { + "docs": "The window id to submit a prompt about", + "type": "string", + }, + }, + "request": { + "body": { + "properties": { + "clientRequestId": "optional", + "costThresholdCredits": "optional", + "followPaginationLinks": { + "default": false, + "docs": "Whether to follow pagination links in the content and load additional results", + "type": "optional", + }, + "prompt": { + "docs": "The prompt to submit about the content in the browser window", + "type": "string", + }, + "timeThresholdSeconds": "optional", + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "PromptContentRequest", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.ModelResponseExternalSessionAiResponseMetadataWrapper", + }, + }, + "scrape-content": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Scrape a window", + "docs": undefined, + "examples": [ + { + "path-parameters": { + "sessionId": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "windowId": "0334da2a-91b0-42c5-6156-76a5eba87430", + }, + "response": { + "body": { + "data": { + "modelResponse": { + "scrapedContent": { + "contentType": "contentType", + "text": "text", + }, + "selectedText": "selectedText", + "title": "title", + }, + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "clientProvided": { + "clientRequestId": "clientRequestId", + "timeThresholdMs": 1000000, + }, + "requestId": "requestId", + "status": "success", + "usage": { + "credits": 1000000, + "id": "id", + }, + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions/{sessionId}/windows/{windowId}/scrape-content", + "path-parameters": { + "sessionId": { + "docs": "The session id to scrape", + "type": "string", + }, + "windowId": { + "docs": "The window id to scrape", + "type": "string", + }, + }, + "response": { + "docs": "Created", + "type": "root.ScrapeModelResponseExternalSessionAiResponseMetadataWrapper", + }, + }, + "summarize-content": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get a summary of content in a browser window", + "docs": undefined, + "examples": [ + { + "path-parameters": { + "sessionId": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "windowId": "0334da2a-91b0-42c5-6156-76a5eba87430", + }, + "request": {}, + "response": { + "body": { + "data": { + "modelResponse": "modelResponse", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "clientProvided": { + "clientRequestId": "clientRequestId", + "timeThresholdMs": 1000000, + }, + "requestId": "requestId", + "status": "success", + "usage": { + "credits": 1000000, + "id": "id", + }, + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/sessions/{sessionId}/windows/{windowId}/summarize-content", + "path-parameters": { + "sessionId": { + "docs": "The session id to summarize", + "type": "string", + }, + "windowId": { + "docs": "The window id to summarize", + "type": "string", + }, + }, + "request": { + "body": { + "properties": { + "clientRequestId": "optional", + "costThresholdCredits": "optional", + "prompt": { + "docs": "The prompt to submit about the content in the browser window", + "type": "optional", + }, + "timeThresholdSeconds": "optional", + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "SummarizeContentRequest", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.ModelResponseExternalSessionAiResponseMetadataWrapper", + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + }, + "packageMarkerFile": { + "errors": { + "ExtensionsGetByIdRequestInternalServerError": { + "docs": "Internal Server Error", + "status-code": 500, + "type": "ErrorEnvelope", + }, + "ExtensionsGetByIdRequestNotFoundError": { + "docs": "Not Found", + "status-code": 404, + "type": "ErrorEnvelope", + }, + "ExtensionsGetByIdRequestUnprocessableEntityError": { + "docs": "Unprocessable Entity", + "status-code": 422, + "type": "ErrorEnvelope", + }, + "ProfilesGetByIdRequestInternalServerError": { + "docs": "Internal Server Error", + "status-code": 500, + "type": "ErrorEnvelope", + }, + "ProfilesGetByIdRequestNotFoundError": { + "docs": "Not Found", + "status-code": 404, + "type": "ErrorEnvelope", + }, + "ProfilesGetByIdRequestUnprocessableEntityError": { + "docs": "Unprocessable Entity", + "status-code": 422, + "type": "ErrorEnvelope", + }, + "SessionsEventsRequestInternalServerError": { + "docs": "Internal Server Error", + "status-code": 500, + "type": "ErrorEnvelope", + }, + "SessionsEventsRequestNotFoundError": { + "docs": "Not Found", + "status-code": 404, + "type": "ErrorEnvelope", + }, + "SessionsEventsRequestUnprocessableEntityError": { + "docs": "Unprocessable Entity", + "status-code": 422, + "type": "ErrorEnvelope", + }, + "SessionsGetInfoRequestInternalServerError": { + "docs": "Internal Server Error", + "status-code": 500, + "type": "ErrorEnvelope", + }, + "SessionsGetInfoRequestNotFoundError": { + "docs": "Not Found", + "status-code": 404, + "type": "ErrorEnvelope", + }, + "SessionsGetInfoRequestUnprocessableEntityError": { + "docs": "Unprocessable Entity", + "status-code": 422, + "type": "ErrorEnvelope", + }, + "SessionsListRequestInternalServerError": { + "docs": "Internal Server Error", + "status-code": 500, + "type": "ErrorEnvelope", + }, + "SessionsListRequestNotFoundError": { + "docs": "Not Found", + "status-code": 404, + "type": "ErrorEnvelope", + }, + "SessionsListRequestUnprocessableEntityError": { + "docs": "Unprocessable Entity", + "status-code": 422, + "type": "ErrorEnvelope", + }, + }, + "types": { + "ClientProvidedResponseMetadata": { + "docs": undefined, + "properties": { + "clientRequestId": "optional", + "timeThresholdMs": "optional", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "DefaultMetaWrapper": { + "docs": undefined, + "properties": { + "requestId": "optional", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ErrorEnvelope": { + "docs": undefined, + "properties": { + "HttpStatus": "long", + "Message": "string", + "data": "unknown", + "errors": "optional>", + "meta": "unknown", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ErrorMessage": { + "docs": undefined, + "properties": { + "code": { + "docs": "Error code", + "type": "string", + }, + "event": { + "docs": "Event name", + "type": "string", + }, + "message": { + "docs": "Error message", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExtensionV1": { + "docs": undefined, + "properties": { + "date_created": { + "docs": "Date the extension was created", + "type": "datetime", + }, + "id": { + "docs": "Unique identifier for the extension", + "type": "string", + }, + "name": { + "docs": "Name of the extension", + "type": "string", + }, + "upload_uri": { + "docs": "URI to upload profile data to", + "type": "optional", + "validation": { + "format": "uri", + "maxLength": undefined, + "minLength": undefined, + "pattern": undefined, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExtensionV1EnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "ExtensionV1", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExternalSessionAiResponseMetadata": { + "docs": undefined, + "properties": { + "clientProvided": "optional", + "requestId": "optional", + "status": "ExternalSessionAiResponseMetadataStatus", + "usage": "ExternalSessionAiResponseMetadataUsage", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExternalSessionAiResponseMetadataStatus": { + "enum": [ + "success", + "partial_success", + "failure", + ], + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExternalSessionAiResponseMetadataUsage": { + "docs": undefined, + "properties": { + "credits": { + "docs": "The credit usage for this request", + "type": "long", + }, + "id": { + "docs": "The id of the request", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "Issue": { + "docs": undefined, + "properties": { + "code": { + "docs": "Issue code", + "type": "optional", + }, + "details": { + "docs": "Any associated details", + "type": "optional>", + }, + "message": { + "docs": "Message describing the issue", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ListExtensionV1EnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "optional>", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ListProfileV1EnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "optional>", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "LiveViewConnectionInfo": { + "docs": undefined, + "properties": { + "airtop_browser_url": { + "docs": "Websocket url to connect to the airtop browser", + "type": "string", + }, + "token": { + "docs": "Token to connect to the airtop browser, use as a header: 'Authorization: Bearer ' ", + "type": "optional", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ModelResponse": { + "docs": undefined, + "properties": { + "modelResponse": "string", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ModelResponseExternalSessionAiResponseMetadataWrapper": { + "docs": undefined, + "properties": { + "data": "ModelResponse", + "errors": "optional>", + "meta": "ExternalSessionAiResponseMetadata", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "Pagination": { + "docs": undefined, + "properties": { + "currentLimit": "long", + "currentPage": "long", + "finalCount": "long", + "hasMore": "boolean", + "initialCount": "long", + "nextOffset": "long", + "numberOfPages": "long", + "totalItems": "long", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ProfileV1": { + "docs": undefined, + "properties": { + "name": { + "docs": "Name of the profile", + "type": "string", + "validation": { + "format": undefined, + "maxLength": 100, + "minLength": 1, + "pattern": undefined, + }, + }, + "status": { + "docs": "Status of the profile", + "type": "optional", + }, + "tags": { + "docs": "a comma separated list of tags for this profile", + "type": "optional>", + }, + "upload_uri": { + "docs": "URI to upload profile data to", + "type": "optional", + "validation": { + "format": "uri", + "maxLength": undefined, + "minLength": undefined, + "pattern": undefined, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ProfileV1EnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "ProfileV1", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ScrapeModelResponse": { + "docs": undefined, + "properties": { + "modelResponse": "ScrapeResponseOutput", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ScrapeModelResponseExternalSessionAiResponseMetadataWrapper": { + "docs": undefined, + "properties": { + "data": "ScrapeModelResponse", + "errors": "optional>", + "meta": "ExternalSessionAiResponseMetadata", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ScrapeResponseContent": { + "docs": undefined, + "properties": { + "contentType": { + "docs": "The mime type of content extracted from the browser window", + "type": "string", + }, + "text": { + "docs": "The text content of the browser window", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ScrapeResponseOutput": { + "docs": undefined, + "properties": { + "scrapedContent": { + "docs": "The scraped content of the browser window", + "type": "ScrapeResponseContent", + }, + "selectedText": { + "docs": "Any text that was highlighted in the browser window", + "type": "optional", + }, + "title": { + "docs": "The title of the browser page", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionConfig": { + "docs": undefined, + "properties": { + "persist_profile": { + "docs": "Persist the profile", + "type": "optional", + }, + "persist_profile_name": { + "docs": "Profile name to persist resulting profile (required when persist_profile is true). Must contain A-Za-z0-9 only", + "type": "optional", + }, + "persist_profile_tags": { + "docs": "Profile tags", + "type": "optional>", + }, + "profile_id": { + "docs": "Id of a profile to use with this session", + "type": "optional", + }, + "timeoutMinutes": { + "default": 10, + "docs": "Max length of session in minutes, after which it will terminate if not already deleted", + "type": "optional", + "validation": { + "exclusiveMax": undefined, + "exclusiveMin": undefined, + "max": 1440, + "min": 1, + "multipleOf": undefined, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionConfigV1": { + "docs": undefined, + "properties": { + "persist_profile": { + "docs": "Persist the profile", + "type": "optional", + }, + "persist_profile_name": { + "docs": "Profile name to persist resulting profile (required when persist_profile is true). Must contain A-Za-z0-9 only", + "type": "optional", + }, + "persist_profile_tags": { + "docs": "Profile tags", + "type": "optional>", + }, + "profile_id": { + "docs": "Id of a profile to use with this session", + "type": "optional", + }, + "timeoutMinutes": { + "default": 10, + "docs": "Max length of session in minutes, after which it will terminate if not already deleted", + "type": "optional", + "validation": { + "exclusiveMax": undefined, + "exclusiveMin": undefined, + "max": 1440, + "min": 1, + "multipleOf": undefined, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionWithConnectionInfo": { + "docs": undefined, + "properties": { + "cdp_url": { + "docs": "Url to connect to chrome devtools protocol port on the airtop browser. Include the header 'Authorization: Bearer .'", + "type": "optional", + }, + "cdp_ws_url": { + "docs": "Websocket url to connect to CDP webSocketDebuggerUrl on the airtop browser. Include the header 'Authorization: Bearer .'", + "type": "optional", + }, + "chromedriver_url": { + "docs": "Websocket url to connect to the chromedriver on the airtop browser. Include the header 'Authorization: Bearer .'", + "type": "optional", + }, + "configuration": { + "docs": "Session configuration", + "type": "SessionConfig", + }, + "current_usage": { + "docs": "Current usage in minutes", + "type": "optional", + }, + "date_created": { + "docs": "Date the session was created", + "type": "optional", + }, + "id": { + "docs": "UUID of the session", + "type": "string", + "validation": { + "format": "uuid", + "maxLength": undefined, + "minLength": undefined, + "pattern": undefined, + }, + }, + "last_activity": { + "docs": "Date of the last activity", + "type": "optional", + }, + "live_view": { + "docs": "Connection info for the live view", + "type": "optional", + }, + "status": { + "docs": "Session status", + "type": "string", + }, + "url": { + "docs": "URL of the session", + "type": "optional", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionWithConnectionInfoEnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "SessionWithConnectionInfo", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionsWithPagination": { + "docs": undefined, + "properties": { + "pagination": { + "docs": "Pagination info", + "type": "Pagination", + }, + "sessions": { + "docs": "List of sessions", + "type": "optional>", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionsWithPaginationEnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "SessionsWithPagination", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "StatusMessage": { + "docs": undefined, + "properties": { + "event": { + "docs": "Event name", + "type": "string", + }, + "eventTimeMillis": { + "docs": "Time of the event in milliseconds since epoch", + "type": "long", + }, + "id": { + "docs": "ID of the session", + "type": "string", + }, + "status": { + "docs": "Status of the session", + "type": "StatusMessageStatus", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "StatusMessageStatus": { + "docs": "Status of the session", + "enum": [ + "awaiting_capacity", + "initializing", + "running", + "ended", + ], + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "Window": { + "docs": undefined, + "properties": { + "liveViewUrl": { + "docs": "Url for live view session", + "type": "string", + }, + "token": { + "docs": "Token for authenticating to a live view session", + "type": "string", + }, + "windowId": { + "docs": "Window ID for live view session", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "WindowEnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "Window", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + }, + "rootApiFile": { + "auth": "BearerAuthScheme", + "auth-schemes": { + "BearerAuthScheme": { + "scheme": "bearer", + "token": { + "name": "apiKey", + }, + }, + }, + "default-environment": "Default", + "display-name": "Browser Control API", + "environments": { + "Default": "https://api.airtop.ai/v1", + }, + "error-discrimination": { + "strategy": "status-code", + }, + "name": "api", + }, +} +`; + +exports[`open api parser > switchboard > simple 1`] = ` +{ + "definitionFiles": { + "auth.yml": { + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "andRedirect": { + "auth": false, + "display-name": "Auth and redirect sessions", + "docs": "Auth and redirect sessions by ID", + "examples": [ + {}, + ], + "method": "GET", + "pagination": undefined, + "path": "/auth", + "request": { + "headers": { + "x-call-type": { + "docs": "call type, e.g. CHROMEDRIVER_EXTERNAL", + "name": "callType", + "type": "optional", + }, + "x-session-id": { + "docs": "session id", + "name": "sessionId", + "type": "optional", + }, + }, + "name": "AuthAndRedirectRequest", + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + "extensions.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "delete": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Delete extensions matching name query", + "docs": "Delete an extension by ID", + "examples": [ + { + "query-parameters": { + "name": "Widget", + }, + }, + ], + "method": "DELETE", + "pagination": undefined, + "path": "/extensions", + "request": { + "name": "ExtensionsDeleteRequest", + "query-parameters": { + "name": { + "docs": "Name to search for, supports regex, case insensitive", + "type": "optional", + }, + }, + }, + }, + "get": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Get extensions", + "docs": "Get extensions, searching by name", + "examples": [ + { + "query-parameters": { + "name": "Widget", + }, + "response": { + "body": { + "data": [ + { + "date_created": "2024-01-15T09:30:00Z", + "id": "73df0106-1781-4b03-a288-e749e1a43481", + "name": "Special Widget", + "upload_uri": "upload_uri", + }, + ], + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/extensions", + "request": { + "name": "ExtensionsGetRequest", + "query-parameters": { + "name": { + "docs": "Name to search for, supports regex, case insensitive", + "type": "optional", + }, + }, + }, + "response": { + "docs": "OK", + "type": "root.ListExtensionV1EnvelopeDefaultMetaWrapper", + }, + }, + "getById": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Get an extension", + "docs": "Get an extension by ID", + "errors": [ + "root.NotFoundError", + "root.UnprocessableEntityError", + "root.InternalServerError", + ], + "examples": [ + { + "path-parameters": { + "id": "4a61a55c-391b-4f73-957e-ffbd29ac7cba", + }, + "response": { + "body": { + "data": { + "date_created": "2024-01-15T09:30:00Z", + "id": "73df0106-1781-4b03-a288-e749e1a43481", + "name": "Special Widget", + "upload_uri": "upload_uri", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/extensions/{id}", + "path-parameters": { + "id": { + "docs": "UUID of the profile to get", + "type": "string", + }, + }, + "response": { + "docs": "OK", + "type": "root.ExtensionV1EnvelopeDefaultMetaWrapper", + }, + }, + "post": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Create an extension", + "docs": undefined, + "examples": [ + { + "request": { + "name": "Special Widget", + }, + "response": { + "body": { + "data": { + "date_created": "2024-01-15T09:30:00Z", + "id": "73df0106-1781-4b03-a288-e749e1a43481", + "name": "Special Widget", + "upload_uri": "upload_uri", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/extensions", + "request": { + "body": { + "properties": { + "name": { + "docs": "Name of the extension", + "type": "string", + }, + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "ExtensionsPostRequest", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.ExtensionV1EnvelopeDefaultMetaWrapper", + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + "profiles.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "delete": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Delete profiles matching query", + "docs": "Delete profiles matching query", + "examples": [ + { + "query-parameters": { + "name": "^Acme.*", + }, + }, + ], + "method": "DELETE", + "pagination": undefined, + "path": "/profiles", + "request": { + "name": "ProfilesDeleteRequest", + "query-parameters": { + "name": { + "docs": "Name to search for, supports regex, case insensitive", + "type": "optional", + }, + "tags": { + "allow-multiple": true, + "docs": "a comma separated list of tags to filter by (joined by OR, e.g. tag is red OR blue)", + "type": "optional", + }, + }, + }, + }, + "get": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get profiles", + "docs": "Get profiles, searching by name or tags", + "examples": [ + { + "query-parameters": { + "name": "^Acme.*", + }, + "response": { + "body": { + "data": [ + { + "name": "Acme corp login", + "status": "status", + "tags": [ + "blue", + "red", + ], + "upload_uri": "upload_uri", + }, + ], + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/profiles", + "request": { + "name": "ProfilesGetRequest", + "query-parameters": { + "name": { + "docs": "Name to search for, supports regex, case insensitive", + "type": "optional", + }, + "tags": { + "allow-multiple": true, + "docs": "a comma separated list of tags to filter by (joined by OR, e.g. tag is red OR blue)", + "type": "optional", + }, + }, + }, + "response": { + "docs": "OK", + "type": "root.ListProfileV1EnvelopeDefaultMetaWrapper", + }, + }, + "getById": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get a profile", + "docs": "Get a profile by ID", + "errors": [ + "root.NotFoundError", + "root.UnprocessableEntityError", + "root.InternalServerError", + ], + "examples": [ + { + "path-parameters": { + "id": "4a61a55c-391b-4f73-957e-ffbd29ac7cba", + }, + "response": { + "body": { + "data": { + "name": "Acme corp login", + "status": "status", + "tags": [ + "blue", + "red", + ], + "upload_uri": "upload_uri", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/profiles/{id}", + "path-parameters": { + "id": { + "docs": "name of the profile to get", + "type": "string", + }, + }, + "response": { + "docs": "OK", + "type": "root.ProfileV1EnvelopeDefaultMetaWrapper", + }, + }, + "post": { + "audiences": [ + "beta", + ], + "auth": false, + "display-name": "Create a profile", + "docs": undefined, + "examples": [ + { + "request": { + "name": "Acme corp login", + }, + "response": { + "body": { + "data": { + "name": "Acme corp login", + "status": "status", + "tags": [ + "blue", + "red", + ], + "upload_uri": "upload_uri", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/profiles", + "request": { + "body": { + "properties": { + "name": { + "docs": "Name of the Profile", + "type": "string", + "validation": { + "format": undefined, + "maxLength": 100, + "minLength": 1, + "pattern": undefined, + }, + }, + "tags": { + "docs": "a comma separated list of tags for this profile", + "type": "optional>", + }, + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "ProfileInput", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.ProfileV1EnvelopeDefaultMetaWrapper", + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + "sessions.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "events": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get a session event stream", + "docs": "Get a session event stream for a given session ID", + "errors": [ + "root.NotFoundError", + "root.UnprocessableEntityError", + "root.InternalServerError", + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions/{id}/events", + "path-parameters": { + "id": { + "docs": "UUID of the session to get status info for", + "type": "string", + }, + }, + "response-stream": { + "docs": "OK", + "format": "sse", + "type": "SessionsEventsResponse", + }, + }, + "getinfo": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get info for a session", + "docs": "Get a session by ID", + "errors": [ + "root.NotFoundError", + "root.UnprocessableEntityError", + "root.InternalServerError", + ], + "examples": [ + { + "path-parameters": { + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + }, + "response": { + "body": { + "data": { + "cdp_url": "cdp_url", + "cdp_ws_url": "cdp_ws_url", + "chromedriver_url": "chromedriver_url", + "configuration": { + "persist_profile": true, + "persist_profile_name": "default", + "persist_profile_tags": [ + "persist_profile_tags", + ], + "profile_id": "linkedin", + "timeoutMinutes": 10, + }, + "current_usage": 1000000, + "date_created": "2024-01-15T09:30:00Z", + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "last_activity": "2024-01-15T09:30:00Z", + "live_view": { + "airtop_browser_url": "airtop_browser_url", + "token": "token", + }, + "status": "active", + "url": "url", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions/{id}", + "path-parameters": { + "id": { + "docs": "UUID of the session to get", + "type": "string", + }, + }, + "response": { + "docs": "OK", + "type": "root.SessionWithConnectionInfoEnvelopeDefaultMetaWrapper", + }, + }, + "list": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get a list of sessions", + "docs": "Get a list of sessions by ID", + "errors": [ + "root.NotFoundError", + "root.UnprocessableEntityError", + "root.InternalServerError", + ], + "examples": [ + { + "query-parameters": { + "limit": 10, + "offset": 1, + }, + "response": { + "body": { + "data": { + "pagination": { + "currentLimit": 1000000, + "currentPage": 1000000, + "finalCount": 1000000, + "hasMore": true, + "initialCount": 1000000, + "nextOffset": 1000000, + "numberOfPages": 1000000, + "totalItems": 1000000, + }, + "sessions": [ + { + "configuration": { + "persist_profile": true, + "persist_profile_name": "default", + "profile_id": "linkedin", + "timeoutMinutes": 10, + }, + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "status": "active", + }, + ], + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions", + "request": { + "name": "SessionsListRequest", + "query-parameters": { + "limit": { + "docs": "limit for pagination", + "type": "optional", + }, + "offset": { + "docs": "offset for pagination", + "type": "optional", + }, + "sessionIds": { + "allow-multiple": true, + "docs": "a comma separated list of UUIDs of the session to get", + "type": "optional", + }, + "status": { + "docs": "status of the session to get", + "type": "optional", + }, + }, + }, + "response": { + "docs": "OK", + "type": "root.SessionsWithPaginationEnvelopeDefaultMetaWrapper", + }, + }, + "post": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Create a session", + "docs": undefined, + "examples": [ + { + "request": {}, + "response": { + "body": { + "data": { + "cdp_url": "cdp_url", + "cdp_ws_url": "cdp_ws_url", + "chromedriver_url": "chromedriver_url", + "configuration": { + "persist_profile": true, + "persist_profile_name": "default", + "persist_profile_tags": [ + "persist_profile_tags", + ], + "profile_id": "linkedin", + "timeoutMinutes": 10, + }, + "current_usage": 1000000, + "date_created": "2024-01-15T09:30:00Z", + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "last_activity": "2024-01-15T09:30:00Z", + "live_view": { + "airtop_browser_url": "airtop_browser_url", + "token": "token", + }, + "status": "active", + "url": "url", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/sessions", + "request": { + "body": { + "properties": { + "configuration": { + "docs": "Session configuration", + "type": "optional", + }, + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "SessionRestInputV1", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.SessionWithConnectionInfoEnvelopeDefaultMetaWrapper", + }, + }, + "terminate": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Ends a session", + "docs": "Ends a session by ID. If a given session id does not exist within the organization, it is ignored.", + "examples": [ + { + "path-parameters": { + "id": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + }, + }, + ], + "method": "DELETE", + "pagination": undefined, + "path": "/sessions/{id}", + "path-parameters": { + "id": { + "docs": "UUID of the session to delete", + "type": "string", + }, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "types": { + "SessionsEventsResponse": { + "availability": undefined, + "base-properties": {}, + "discriminant": "event", + "docs": "Each oneOf object in the array represents one possible Server Sent Events (SSE) message, serialized as UTF-8 text according to the SSE specification.", + "encoding": undefined, + "source": { + "openapi": "switchboard/openapi.yml", + }, + "union": { + "error": { + "type": "SessionsEventsResponseError", + }, + "status": { + "type": "SessionsEventsResponseStatus", + }, + }, + }, + "SessionsEventsResponseError": { + "docs": undefined, + "properties": { + "data": "root.ErrorMessage", + "id": { + "docs": "The event ID.", + "type": "optional", + }, + "retry": { + "docs": "The retry time in milliseconds.", + "type": "optional", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionsEventsResponseStatus": { + "docs": undefined, + "properties": { + "data": "root.StatusMessage", + "id": { + "docs": "The event ID.", + "type": "optional", + }, + "retry": { + "docs": "The retry time in milliseconds.", + "type": "optional", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionsListRequestStatus": { + "docs": "status of the session to get", + "enum": [ + "awaiting_capacity", + "initializing", + "running", + "ended", + ], + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + }, + "windows.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "get-window-info": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get information about a browser window in a session", + "docs": undefined, + "examples": [ + { + "path-parameters": { + "sessionId": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "windowId": "7334da2a-91b0-42c5-6156-76a5eba87430", + }, + "query-parameters": { + "Screen resolution": "1920x1080", + }, + "response": { + "body": { + "data": { + "liveViewUrl": "liveViewUrl", + "token": "token", + "windowId": "windowId", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "requestId": "requestId", + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions/{sessionId}/windows/{windowId}/get-window-info", + "path-parameters": { + "sessionId": { + "docs": "UUID of the session that owns the window", + "type": "string", + }, + "windowId": { + "docs": "id of the browser window, either the windowId (uuid) or targetId (simple string)", + "type": "string", + }, + }, + "request": { + "name": "GetWindowInfoRequest", + "query-parameters": { + "Screen resolution": { + "docs": "Ex: Screen resolution of the live view in format wxh, e.g. 1920x1080. When this value is provided the live view won't adapt it's resolution to the live view window size", + "type": "optional", + }, + "disableResize": { + "docs": "By default browsers will adapt its resolution to the live view window size. This flag disables that behavior", + "type": "optional", + }, + "includeNavigationBar": { + "docs": "Renders a navigation bar as part of the live view session", + "type": "optional", + }, + }, + }, + "response": { + "docs": "Created", + "type": "root.WindowEnvelopeDefaultMetaWrapper", + }, + }, + "prompt-content": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Submit a prompt about the content in a specific browser window.", + "docs": undefined, + "examples": [ + { + "path-parameters": { + "sessionId": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "windowId": "0334da2a-91b0-42c5-6156-76a5eba87430", + }, + "request": { + "prompt": "What is the main idea of this page?", + }, + "response": { + "body": { + "data": { + "modelResponse": "modelResponse", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "clientProvided": { + "clientRequestId": "clientRequestId", + "timeThresholdMs": 1000000, + }, + "requestId": "requestId", + "status": "success", + "usage": { + "credits": 1000000, + "id": "id", + }, + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/sessions/{sessionId}/windows/{windowId}/prompt-content", + "path-parameters": { + "sessionId": { + "docs": "The session id to submit a prompt about", + "type": "string", + }, + "windowId": { + "docs": "The window id to submit a prompt about", + "type": "string", + }, + }, + "request": { + "body": { + "properties": { + "clientRequestId": "optional", + "costThresholdCredits": "optional", + "followPaginationLinks": { + "default": false, + "docs": "Whether to follow pagination links in the content and load additional results", + "type": "optional", + }, + "prompt": { + "docs": "The prompt to submit about the content in the browser window", + "type": "string", + }, + "timeThresholdSeconds": "optional", + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "PromptContentRequest", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.ModelResponseExternalSessionAiResponseMetadataWrapper", + }, + }, + "scrape-content": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Scrape a window", + "docs": undefined, + "examples": [ + { + "path-parameters": { + "sessionId": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "windowId": "0334da2a-91b0-42c5-6156-76a5eba87430", + }, + "response": { + "body": { + "data": { + "modelResponse": { + "scrapedContent": { + "contentType": "contentType", + "text": "text", + }, + "selectedText": "selectedText", + "title": "title", + }, + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "clientProvided": { + "clientRequestId": "clientRequestId", + "timeThresholdMs": 1000000, + }, + "requestId": "requestId", + "status": "success", + "usage": { + "credits": 1000000, + "id": "id", + }, + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "GET", + "pagination": undefined, + "path": "/sessions/{sessionId}/windows/{windowId}/scrape-content", + "path-parameters": { + "sessionId": { + "docs": "The session id to scrape", + "type": "string", + }, + "windowId": { + "docs": "The window id to scrape", + "type": "string", + }, + }, + "response": { + "docs": "Created", + "type": "root.ScrapeModelResponseExternalSessionAiResponseMetadataWrapper", + }, + }, + "summarize-content": { + "audiences": [ + "public", + ], + "auth": false, + "display-name": "Get a summary of content in a browser window", + "docs": undefined, + "examples": [ + { + "path-parameters": { + "sessionId": "6aac6f73-bd89-4a76-ab32-5a6c422e8b0b", + "windowId": "0334da2a-91b0-42c5-6156-76a5eba87430", + }, + "request": {}, + "response": { + "body": { + "data": { + "modelResponse": "modelResponse", + }, + "errors": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + "meta": { + "clientProvided": { + "clientRequestId": "clientRequestId", + "timeThresholdMs": 1000000, + }, + "requestId": "requestId", + "status": "success", + "usage": { + "credits": 1000000, + "id": "id", + }, + }, + "warnings": [ + { + "code": "code", + "details": { + "key": "value", + }, + "message": "message", + }, + ], + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/sessions/{sessionId}/windows/{windowId}/summarize-content", + "path-parameters": { + "sessionId": { + "docs": "The session id to summarize", + "type": "string", + }, + "windowId": { + "docs": "The window id to summarize", + "type": "string", + }, + }, + "request": { + "body": { + "properties": { + "clientRequestId": "optional", + "costThresholdCredits": "optional", + "prompt": { + "docs": "The prompt to submit about the content in the browser window", + "type": "optional", + }, + "timeThresholdSeconds": "optional", + }, + }, + "content-type": "application/json", + "headers": undefined, + "name": "SummarizeContentRequest", + "query-parameters": undefined, + }, + "response": { + "docs": "Created", + "type": "root.ModelResponseExternalSessionAiResponseMetadataWrapper", + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + }, + "packageMarkerFile": { + "errors": { + "InternalServerError": { + "docs": "Internal Server Error", + "status-code": 500, + "type": "ErrorEnvelope", + }, + "NotFoundError": { + "docs": "Not Found", + "status-code": 404, + "type": "ErrorEnvelope", + }, + "UnprocessableEntityError": { + "docs": "Unprocessable Entity", + "status-code": 422, + "type": "ErrorEnvelope", + }, + }, + "types": { + "ClientProvidedResponseMetadata": { + "docs": undefined, + "properties": { + "clientRequestId": "optional", + "timeThresholdMs": "optional", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "DefaultMetaWrapper": { + "docs": undefined, + "properties": { + "requestId": "optional", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ErrorEnvelope": { + "docs": undefined, + "properties": { + "HttpStatus": "long", + "Message": "string", + "data": "unknown", + "errors": "optional>", + "meta": "unknown", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ErrorMessage": { + "docs": undefined, + "properties": { + "code": { + "docs": "Error code", + "type": "string", + }, + "event": { + "docs": "Event name", + "type": "string", + }, + "message": { + "docs": "Error message", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExtensionV1": { + "docs": undefined, + "properties": { + "date_created": { + "docs": "Date the extension was created", + "type": "datetime", + }, + "id": { + "docs": "Unique identifier for the extension", + "type": "string", + }, + "name": { + "docs": "Name of the extension", + "type": "string", + }, + "upload_uri": { + "docs": "URI to upload profile data to", + "type": "optional", + "validation": { + "format": "uri", + "maxLength": undefined, + "minLength": undefined, + "pattern": undefined, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExtensionV1EnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "ExtensionV1", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExternalSessionAiResponseMetadata": { + "docs": undefined, + "properties": { + "clientProvided": "optional", + "requestId": "optional", + "status": "ExternalSessionAiResponseMetadataStatus", + "usage": "ExternalSessionAiResponseMetadataUsage", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExternalSessionAiResponseMetadataStatus": { + "enum": [ + "success", + "partial_success", + "failure", + ], + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ExternalSessionAiResponseMetadataUsage": { + "docs": undefined, + "properties": { + "credits": { + "docs": "The credit usage for this request", + "type": "long", + }, + "id": { + "docs": "The id of the request", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "Issue": { + "docs": undefined, + "properties": { + "code": { + "docs": "Issue code", + "type": "optional", + }, + "details": { + "docs": "Any associated details", + "type": "optional>", + }, + "message": { + "docs": "Message describing the issue", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ListExtensionV1EnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "optional>", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ListProfileV1EnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "optional>", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "LiveViewConnectionInfo": { + "docs": undefined, + "properties": { + "airtop_browser_url": { + "docs": "Websocket url to connect to the airtop browser", + "type": "string", + }, + "token": { + "docs": "Token to connect to the airtop browser, use as a header: 'Authorization: Bearer ' ", + "type": "optional", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ModelResponse": { + "docs": undefined, + "properties": { + "modelResponse": "string", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ModelResponseExternalSessionAiResponseMetadataWrapper": { + "docs": undefined, + "properties": { + "data": "ModelResponse", + "errors": "optional>", + "meta": "ExternalSessionAiResponseMetadata", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "Pagination": { + "docs": undefined, + "properties": { + "currentLimit": "long", + "currentPage": "long", + "finalCount": "long", + "hasMore": "boolean", + "initialCount": "long", + "nextOffset": "long", + "numberOfPages": "long", + "totalItems": "long", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ProfileV1": { + "docs": undefined, + "properties": { + "name": { + "docs": "Name of the profile", + "type": "string", + "validation": { + "format": undefined, + "maxLength": 100, + "minLength": 1, + "pattern": undefined, + }, + }, + "status": { + "docs": "Status of the profile", + "type": "optional", + }, + "tags": { + "docs": "a comma separated list of tags for this profile", + "type": "optional>", + }, + "upload_uri": { + "docs": "URI to upload profile data to", + "type": "optional", + "validation": { + "format": "uri", + "maxLength": undefined, + "minLength": undefined, + "pattern": undefined, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ProfileV1EnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "ProfileV1", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ScrapeModelResponse": { + "docs": undefined, + "properties": { + "modelResponse": "ScrapeResponseOutput", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ScrapeModelResponseExternalSessionAiResponseMetadataWrapper": { + "docs": undefined, + "properties": { + "data": "ScrapeModelResponse", + "errors": "optional>", + "meta": "ExternalSessionAiResponseMetadata", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ScrapeResponseContent": { + "docs": undefined, + "properties": { + "contentType": { + "docs": "The mime type of content extracted from the browser window", + "type": "string", + }, + "text": { + "docs": "The text content of the browser window", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "ScrapeResponseOutput": { + "docs": undefined, + "properties": { + "scrapedContent": { + "docs": "The scraped content of the browser window", + "type": "ScrapeResponseContent", + }, + "selectedText": { + "docs": "Any text that was highlighted in the browser window", + "type": "optional", + }, + "title": { + "docs": "The title of the browser page", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionConfig": { + "docs": undefined, + "properties": { + "persist_profile": { + "docs": "Persist the profile", + "type": "optional", + }, + "persist_profile_name": { + "docs": "Profile name to persist resulting profile (required when persist_profile is true). Must contain A-Za-z0-9 only", + "type": "optional", + }, + "persist_profile_tags": { + "docs": "Profile tags", + "type": "optional>", + }, + "profile_id": { + "docs": "Id of a profile to use with this session", + "type": "optional", + }, + "timeoutMinutes": { + "default": 10, + "docs": "Max length of session in minutes, after which it will terminate if not already deleted", + "type": "optional", + "validation": { + "exclusiveMax": undefined, + "exclusiveMin": undefined, + "max": 1440, + "min": 1, + "multipleOf": undefined, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionConfigV1": { + "docs": undefined, + "properties": { + "persist_profile": { + "docs": "Persist the profile", + "type": "optional", + }, + "persist_profile_name": { + "docs": "Profile name to persist resulting profile (required when persist_profile is true). Must contain A-Za-z0-9 only", + "type": "optional", + }, + "persist_profile_tags": { + "docs": "Profile tags", + "type": "optional>", + }, + "profile_id": { + "docs": "Id of a profile to use with this session", + "type": "optional", + }, + "timeoutMinutes": { + "default": 10, + "docs": "Max length of session in minutes, after which it will terminate if not already deleted", + "type": "optional", + "validation": { + "exclusiveMax": undefined, + "exclusiveMin": undefined, + "max": 1440, + "min": 1, + "multipleOf": undefined, + }, + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionWithConnectionInfo": { + "docs": undefined, + "properties": { + "cdp_url": { + "docs": "Url to connect to chrome devtools protocol port on the airtop browser. Include the header 'Authorization: Bearer .'", + "type": "optional", + }, + "cdp_ws_url": { + "docs": "Websocket url to connect to CDP webSocketDebuggerUrl on the airtop browser. Include the header 'Authorization: Bearer .'", + "type": "optional", + }, + "chromedriver_url": { + "docs": "Websocket url to connect to the chromedriver on the airtop browser. Include the header 'Authorization: Bearer .'", + "type": "optional", + }, + "configuration": { + "docs": "Session configuration", + "type": "SessionConfig", + }, + "current_usage": { + "docs": "Current usage in minutes", + "type": "optional", + }, + "date_created": { + "docs": "Date the session was created", + "type": "optional", + }, + "id": { + "docs": "UUID of the session", + "type": "string", + "validation": { + "format": "uuid", + "maxLength": undefined, + "minLength": undefined, + "pattern": undefined, + }, + }, + "last_activity": { + "docs": "Date of the last activity", + "type": "optional", + }, + "live_view": { + "docs": "Connection info for the live view", + "type": "optional", + }, + "status": { + "docs": "Session status", + "type": "string", + }, + "url": { + "docs": "URL of the session", + "type": "optional", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionWithConnectionInfoEnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "SessionWithConnectionInfo", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionsWithPagination": { + "docs": undefined, + "properties": { + "pagination": { + "docs": "Pagination info", + "type": "Pagination", + }, + "sessions": { + "docs": "List of sessions", + "type": "optional>", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "SessionsWithPaginationEnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "SessionsWithPagination", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "StatusMessage": { + "docs": undefined, + "properties": { + "event": { + "docs": "Event name", + "type": "string", + }, + "eventTimeMillis": { + "docs": "Time of the event in milliseconds since epoch", + "type": "long", + }, + "id": { + "docs": "ID of the session", + "type": "string", + }, + "status": { + "docs": "Status of the session", + "type": "StatusMessageStatus", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "StatusMessageStatus": { + "docs": "Status of the session", + "enum": [ + "awaiting_capacity", + "initializing", + "running", + "ended", + ], + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "Window": { + "docs": undefined, + "properties": { + "liveViewUrl": { + "docs": "Url for live view session", + "type": "string", + }, + "token": { + "docs": "Token for authenticating to a live view session", + "type": "string", + }, + "windowId": { + "docs": "Window ID for live view session", + "type": "string", + }, + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + "WindowEnvelopeDefaultMetaWrapper": { + "docs": undefined, + "properties": { + "data": "Window", + "errors": "optional>", + "meta": "DefaultMetaWrapper", + "warnings": "optional>", + }, + "source": { + "openapi": "switchboard/openapi.yml", + }, + }, + }, + }, + "rootApiFile": { + "auth": "BearerAuthScheme", + "auth-schemes": { + "BearerAuthScheme": { + "scheme": "bearer", + "token": { + "name": "apiKey", + }, + }, + }, + "default-environment": "Default", + "display-name": "Browser Control API", + "environments": { + "Default": "https://api.airtop.ai/v1", + }, + "error-discrimination": { + "strategy": "status-code", + }, + "name": "api", + }, +} +`; diff --git a/packages/cli/openapi-ir-to-fern-tests/src/__test__/fixtures/switchboard/openapi.yml b/packages/cli/openapi-ir-to-fern-tests/src/__test__/fixtures/switchboard/openapi.yml new file mode 100644 index 00000000000..9e42e11fe13 --- /dev/null +++ b/packages/cli/openapi-ir-to-fern-tests/src/__test__/fixtures/switchboard/openapi.yml @@ -0,0 +1,1563 @@ +components: + schemas: + ClientProvidedResponseMetadata: + additionalProperties: false + properties: + clientRequestId: + type: string + timeThresholdMs: + format: int64 + type: integer + type: object + DefaultMetaWrapper: + additionalProperties: false + properties: + requestId: + type: string + type: object + ErrorEnvelope: + additionalProperties: false + properties: + HttpStatus: + format: int64 + type: integer + Message: + type: string + data: {} + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: {} + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - HttpStatus + - Message + - meta + - data + - errors + - warnings + type: object + ErrorMessage: + additionalProperties: false + properties: + code: + description: Error code + type: string + event: + description: Event name + type: string + message: + description: Error message + type: string + required: + - event + - message + - code + type: object + ExtensionV1: + additionalProperties: false + properties: + date_created: + description: Date the extension was created + format: date-time + type: string + id: + description: Unique identifier for the extension + example: 73df0106-1781-4b03-a288-e749e1a43481 + type: string + name: + description: Name of the extension + example: Special Widget + type: string + upload_uri: + description: URI to upload profile data to + format: uri + type: string + required: + - id + - name + - date_created + type: object + ExtensionV1EnvelopeDefaultMetaWrapper: + additionalProperties: false + properties: + data: + $ref: "#/components/schemas/ExtensionV1" + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/DefaultMetaWrapper" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + Extensions-postRequest: + additionalProperties: false + properties: + name: + description: Name of the extension + example: Special Widget + type: string + required: + - name + type: object + ExternalSessionAiResponseMetadata: + additionalProperties: false + properties: + clientProvided: + $ref: "#/components/schemas/ClientProvidedResponseMetadata" + requestId: + type: string + status: + enum: + - success + - partial_success + - failure + type: string + usage: + $ref: "#/components/schemas/ExternalSessionAiResponseMetadataUsage" + required: + - status + - usage + type: object + ExternalSessionAiResponseMetadataUsage: + additionalProperties: false + properties: + credits: + description: The credit usage for this request + format: int64 + type: integer + id: + description: The id of the request + type: string + required: + - id + - credits + type: object + Issue: + additionalProperties: false + properties: + code: + description: Issue code + nullable: true + type: string + details: + additionalProperties: {} + description: Any associated details + nullable: true + type: object + message: + description: Message describing the issue + type: string + required: + - message + type: object + ListExtensionV1EnvelopeDefaultMetaWrapper: + additionalProperties: false + properties: + data: + items: + $ref: "#/components/schemas/ExtensionV1" + nullable: true + type: array + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/DefaultMetaWrapper" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + ListProfileV1EnvelopeDefaultMetaWrapper: + additionalProperties: false + properties: + data: + items: + $ref: "#/components/schemas/ProfileV1" + nullable: true + type: array + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/DefaultMetaWrapper" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + LiveViewConnectionInfo: + additionalProperties: false + properties: + airtop_browser_url: + description: Websocket url to connect to the airtop browser + type: string + token: + description: "Token to connect to the airtop browser, use as a header: 'Authorization: Bearer ' " + type: string + required: + - airtop_browser_url + type: object + ModelResponse: + additionalProperties: false + properties: + modelResponse: + type: string + required: + - modelResponse + type: object + ModelResponseExternalSessionAiResponseMetadataWrapper: + additionalProperties: false + properties: + data: + $ref: "#/components/schemas/ModelResponse" + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/ExternalSessionAiResponseMetadata" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + Pagination: + additionalProperties: false + properties: + currentLimit: + format: int64 + type: integer + currentPage: + format: int64 + type: integer + finalCount: + format: int64 + type: integer + hasMore: + type: boolean + initialCount: + format: int64 + type: integer + nextOffset: + format: int64 + type: integer + numberOfPages: + format: int64 + type: integer + totalItems: + format: int64 + type: integer + required: + - currentLimit + - currentPage + - finalCount + - hasMore + - initialCount + - nextOffset + - numberOfPages + - totalItems + type: object + ProfileInput: + additionalProperties: false + properties: + name: + description: Name of the Profile + example: Acme corp login + maxLength: 100 + minLength: 1 + type: string + tags: + description: a comma separated list of tags for this profile + example: + - yellow + - orange + items: + type: string + nullable: true + type: array + uniqueItems: true + required: + - name + type: object + ProfileV1: + additionalProperties: false + properties: + name: + description: Name of the profile + example: Acme corp login + maxLength: 100 + minLength: 1 + type: string + status: + description: Status of the profile + type: string + tags: + description: a comma separated list of tags for this profile + example: + - blue + - red + items: + type: string + nullable: true + type: array + uniqueItems: true + upload_uri: + description: URI to upload profile data to + format: uri + type: string + required: + - name + - tags + type: object + ProfileV1EnvelopeDefaultMetaWrapper: + additionalProperties: false + properties: + data: + $ref: "#/components/schemas/ProfileV1" + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/DefaultMetaWrapper" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + Prompt-contentRequest: + additionalProperties: false + properties: + clientRequestId: + type: string + costThresholdCredits: + format: float + type: number + followPaginationLinks: + default: false + description: Whether to follow pagination links in the content and load additional results + example: false + type: boolean + prompt: + description: The prompt to submit about the content in the browser window + example: What is the main idea of this page? + type: string + timeThresholdSeconds: + format: int64 + type: integer + required: + - prompt + type: object + ScrapeModelResponse: + additionalProperties: false + properties: + modelResponse: + $ref: "#/components/schemas/ScrapeResponseOutput" + required: + - modelResponse + type: object + ScrapeModelResponseExternalSessionAiResponseMetadataWrapper: + additionalProperties: false + properties: + data: + $ref: "#/components/schemas/ScrapeModelResponse" + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/ExternalSessionAiResponseMetadata" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + ScrapeResponseContent: + additionalProperties: false + properties: + contentType: + description: The mime type of content extracted from the browser window + type: string + text: + description: The text content of the browser window + type: string + required: + - contentType + - text + type: object + ScrapeResponseOutput: + additionalProperties: false + properties: + scrapedContent: + $ref: "#/components/schemas/ScrapeResponseContent" + description: The scraped content of the browser window + selectedText: + description: Any text that was highlighted in the browser window + type: string + title: + description: The title of the browser page + type: string + required: + - scrapedContent + - title + type: object + SessionConfig: + additionalProperties: false + properties: + persist_profile: + description: Persist the profile + example: true + type: boolean + persist_profile_name: + description: Profile name to persist resulting profile (required when persist_profile is true). Must contain A-Za-z0-9 only + example: default + type: string + persist_profile_tags: + description: Profile tags + items: + type: string + nullable: true + type: array + profile_id: + description: Id of a profile to use with this session + example: linkedin + type: string + timeoutMinutes: + default: 10 + description: Max length of session in minutes, after which it will terminate if not already deleted + example: 10 + format: int + maximum: 1440 + minimum: 1 + type: integer + type: object + SessionConfigV1: + additionalProperties: false + properties: + persist_profile: + description: Persist the profile + example: true + type: boolean + persist_profile_name: + description: Profile name to persist resulting profile (required when persist_profile is true). Must contain A-Za-z0-9 only + example: default + type: string + persist_profile_tags: + description: Profile tags + items: + type: string + nullable: true + type: array + profile_id: + description: Id of a profile to use with this session + example: linkedin + type: string + timeoutMinutes: + default: 10 + description: Max length of session in minutes, after which it will terminate if not already deleted + example: 10 + format: int + maximum: 1440 + minimum: 1 + type: integer + type: object + SessionRestInputV1: + additionalProperties: false + properties: + configuration: + $ref: "#/components/schemas/SessionConfigV1" + description: Session configuration + type: object + SessionWithConnectionInfo: + additionalProperties: false + properties: + cdp_url: + description: "Url to connect to chrome devtools protocol port on the airtop browser. Include the header 'Authorization: Bearer .'" + type: string + cdp_ws_url: + description: "Websocket url to connect to CDP webSocketDebuggerUrl on the airtop browser. Include the header 'Authorization: Bearer .'" + type: string + chromedriver_url: + description: "Websocket url to connect to the chromedriver on the airtop browser. Include the header 'Authorization: Bearer .'" + type: string + configuration: + $ref: "#/components/schemas/SessionConfig" + description: Session configuration + current_usage: + description: Current usage in minutes + format: int64 + type: integer + date_created: + description: Date the session was created + format: date-time + type: string + id: + description: UUID of the session + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + format: uuid + type: string + last_activity: + description: Date of the last activity + format: date-time + type: string + live_view: + $ref: "#/components/schemas/LiveViewConnectionInfo" + description: Connection info for the live view + status: + description: Session status + example: active + type: string + url: + description: URL of the session + type: string + required: + - id + - status + - configuration + type: object + SessionWithConnectionInfoEnvelopeDefaultMetaWrapper: + additionalProperties: false + properties: + data: + $ref: "#/components/schemas/SessionWithConnectionInfo" + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/DefaultMetaWrapper" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + SessionsWithPagination: + additionalProperties: false + properties: + pagination: + $ref: "#/components/schemas/Pagination" + description: Pagination info + sessions: + description: List of sessions + items: + $ref: "#/components/schemas/SessionWithConnectionInfo" + nullable: true + type: array + required: + - sessions + - pagination + type: object + SessionsWithPaginationEnvelopeDefaultMetaWrapper: + additionalProperties: false + properties: + data: + $ref: "#/components/schemas/SessionsWithPagination" + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/DefaultMetaWrapper" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + StatusMessage: + additionalProperties: false + properties: + event: + description: Event name + type: string + eventTimeMillis: + description: Time of the event in milliseconds since epoch + format: int64 + type: integer + id: + description: ID of the session + type: string + status: + description: Status of the session + enum: + - awaiting_capacity + - initializing + - running + - ended + type: string + required: + - event + - id + - status + - eventTimeMillis + type: object + Summarize-contentRequest: + additionalProperties: false + properties: + clientRequestId: + type: string + costThresholdCredits: + format: float + type: number + prompt: + description: The prompt to submit about the content in the browser window + example: What is the main idea of this page? + type: string + timeThresholdSeconds: + format: int64 + type: integer + type: object + Window: + additionalProperties: false + properties: + liveViewUrl: + description: Url for live view session + type: string + token: + description: Token for authenticating to a live view session + type: string + windowId: + description: Window ID for live view session + type: string + required: + - liveViewUrl + - windowId + - token + type: object + WindowEnvelopeDefaultMetaWrapper: + additionalProperties: false + properties: + data: + $ref: "#/components/schemas/Window" + errors: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + meta: + $ref: "#/components/schemas/DefaultMetaWrapper" + warnings: + items: + $ref: "#/components/schemas/Issue" + nullable: true + type: array + required: + - meta + - data + - errors + - warnings + type: object + securitySchemes: + BearerAuth: + scheme: bearer + type: http + x-fern-bearer: + name: apiKey +info: + title: Browser Control API + version: 1.0.0 +openapi: 3.0.3 +paths: + /auth: + get: + description: Auth and redirect sessions by ID + operationId: auth-and-redirect + parameters: + - description: session id + in: header + name: x-session-id + schema: + description: session id + format: uuid + type: string + - description: call type, e.g. CHROMEDRIVER_EXTERNAL + in: header + name: x-call-type + schema: + description: call type, e.g. CHROMEDRIVER_EXTERNAL + type: string + responses: + "204": + description: No Content + headers: + Authorization: + schema: + description: airtop api key in Bearer format + type: string + x-session-id: + schema: + description: session id + format: uuid + type: string + x-target-url: + schema: + description: target IP/port and path (if necessary) + type: string + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Auth and redirect sessions + tags: + - Auth + /extensions: + delete: + description: Delete an extension by ID + operationId: extensions-delete + parameters: + - description: Name to search for, supports regex, case insensitive + example: Widget + explode: false + in: query + name: name + schema: + description: Name to search for, supports regex, case insensitive + example: Widget + type: string + responses: + "204": + description: No Content + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Delete extensions matching name query + tags: + - Extensions + x-fern-audiences: + - beta + get: + description: Get extensions, searching by name + operationId: extensions-get + parameters: + - description: Name to search for, supports regex, case insensitive + example: Widget + explode: false + in: query + name: name + schema: + description: Name to search for, supports regex, case insensitive + example: Widget + type: string + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ListExtensionV1EnvelopeDefaultMetaWrapper" + description: List of extensions + description: OK + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Get extensions + tags: + - Extensions + x-fern-audiences: + - beta + post: + operationId: extensions-post + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Extensions-postRequest" + required: true + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/ExtensionV1EnvelopeDefaultMetaWrapper" + description: Extension + description: Created + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Create an extension + tags: + - Extensions + x-fern-audiences: + - beta + /extensions/{id}: + get: + description: Get an extension by ID + operationId: extensions-get-by-id + parameters: + - description: UUID of the profile to get + example: 4a61a55c-391b-4f73-957e-ffbd29ac7cba + in: path + name: id + required: true + schema: + description: UUID of the profile to get + example: 4a61a55c-391b-4f73-957e-ffbd29ac7cba + format: uuid + type: string + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ExtensionV1EnvelopeDefaultMetaWrapper" + description: Extension + description: OK + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Not Found + "422": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Unprocessable Entity + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Internal Server Error + summary: Get an extension + tags: + - Extensions + x-fern-audiences: + - beta + /profiles: + delete: + description: Delete profiles matching query + operationId: profiles-delete + parameters: + - description: Name to search for, supports regex, case insensitive + example: ^Acme.* + explode: false + in: query + name: name + schema: + description: Name to search for, supports regex, case insensitive + example: ^Acme.* + type: string + - description: a comma separated list of tags to filter by (joined by OR, e.g. tag is red OR blue) + example: + - blue + - red + explode: false + in: query + name: tags + schema: + description: a comma separated list of tags to filter by (joined by OR, e.g. tag is red OR blue) + example: + - blue + - red + items: + type: string + nullable: true + type: array + uniqueItems: true + responses: + "204": + description: No Content + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Delete profiles matching query + tags: + - Profiles + x-fern-audiences: + - public + get: + description: Get profiles, searching by name or tags + operationId: profiles-get + parameters: + - description: Name to search for, supports regex, case insensitive + example: ^Acme.* + explode: false + in: query + name: name + schema: + description: Name to search for, supports regex, case insensitive + example: ^Acme.* + type: string + - description: a comma separated list of tags to filter by (joined by OR, e.g. tag is red OR blue) + example: + - blue + - red + explode: false + in: query + name: tags + schema: + description: a comma separated list of tags to filter by (joined by OR, e.g. tag is red OR blue) + example: + - blue + - red + items: + type: string + nullable: true + type: array + uniqueItems: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ListProfileV1EnvelopeDefaultMetaWrapper" + description: OK + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Get profiles + tags: + - Profiles + x-fern-audiences: + - public + post: + operationId: profiles-post + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ProfileInput" + description: Profile + required: true + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/ProfileV1EnvelopeDefaultMetaWrapper" + description: created Profile + description: Created + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Create a profile + tags: + - Profiles + x-fern-audiences: + - beta + /profiles/{id}: + get: + description: Get a profile by ID + operationId: profiles-get-by-id + parameters: + - description: name of the profile to get + example: 4a61a55c-391b-4f73-957e-ffbd29ac7cba + in: path + name: id + required: true + schema: + description: name of the profile to get + example: 4a61a55c-391b-4f73-957e-ffbd29ac7cba + type: string + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ProfileV1EnvelopeDefaultMetaWrapper" + description: created Profile + description: OK + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Not Found + "422": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Unprocessable Entity + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Internal Server Error + summary: Get a profile + tags: + - Profiles + x-fern-audiences: + - public + /sessions: + get: + description: Get a list of sessions by ID + operationId: sessions-list + parameters: + - description: a comma separated list of UUIDs of the session to get + example: + - 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + - e39522ce-44f0-456f-9efd-07b3d4fdd9f2 + explode: false + in: query + name: sessionIds + schema: + description: a comma separated list of UUIDs of the session to get + example: + - 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + - e39522ce-44f0-456f-9efd-07b3d4fdd9f2 + items: + type: string + nullable: true + type: array + - description: status of the session to get + example: running + explode: false + in: query + name: status + schema: + description: status of the session to get + enum: + - awaiting_capacity + - initializing + - running + - ended + example: running + format: enum + type: string + - description: offset for pagination + example: 1 + explode: false + in: query + name: offset + schema: + default: -1 + description: offset for pagination + example: 1 + format: int64 + type: integer + - description: limit for pagination + example: 10 + explode: false + in: query + name: limit + schema: + default: 0 + description: limit for pagination + example: 10 + format: int64 + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/SessionsWithPaginationEnvelopeDefaultMetaWrapper" + description: List of sessions + description: OK + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Not Found + "422": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Unprocessable Entity + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Internal Server Error + summary: Get a list of sessions + tags: + - Sessions + x-fern-audiences: + - public + post: + operationId: sessions-post + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SessionRestInputV1" + description: Session + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/SessionWithConnectionInfoEnvelopeDefaultMetaWrapper" + description: Session data + description: Created + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Create a session + tags: + - Sessions + x-fern-audiences: + - public + /sessions/{id}: + delete: + description: Ends a session by ID. If a given session id does not exist within the organization, it is ignored. + operationId: sessions-terminate + parameters: + - description: UUID of the session to delete + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + in: path + name: id + required: true + schema: + description: UUID of the session to delete + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + type: string + responses: + "204": + description: No Content + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Ends a session + tags: + - Sessions + x-fern-audiences: + - public + get: + description: Get a session by ID + operationId: sessions-getInfo + parameters: + - description: UUID of the session to get + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + in: path + name: id + required: true + schema: + description: UUID of the session to get + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + format: uuid + type: string + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/SessionWithConnectionInfoEnvelopeDefaultMetaWrapper" + description: Session data + description: OK + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Not Found + "422": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Unprocessable Entity + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Internal Server Error + summary: Get info for a session + tags: + - Sessions + x-fern-audiences: + - public + /sessions/{id}/events: + get: + description: Get a session event stream for a given session ID + operationId: sessions-events + parameters: + - description: UUID of the session to get status info for + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + in: path + name: id + required: true + schema: + description: UUID of the session to get status info for + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + type: string + responses: + "200": + content: + text/event-stream: + schema: + description: Each oneOf object in the array represents one possible Server Sent Events (SSE) message, serialized as UTF-8 text according to the SSE specification. + oneOf: + - properties: + data: + $ref: "#/components/schemas/StatusMessage" + event: + const: status + description: The event name. + type: string + id: + description: The event ID. + type: integer + retry: + description: The retry time in milliseconds. + type: integer + required: + - data + - event + title: Event status + type: object + - properties: + data: + $ref: "#/components/schemas/ErrorMessage" + event: + const: error + description: The event name. + type: string + id: + description: The event ID. + type: integer + retry: + description: The retry time in milliseconds. + type: integer + required: + - data + - event + title: Event error + type: object + title: Server Sent Events + type: object + description: OK + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Not Found + "422": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Unprocessable Entity + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Internal Server Error + summary: Get a session event stream + tags: + - Sessions + x-fern-audiences: + - public + x-fern-streaming: + format: sse + /sessions/{sessionId}/windows/{windowId}/get-window-info: + get: + operationId: get-window-info + parameters: + - description: UUID of the session that owns the window + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + in: path + name: sessionId + required: true + schema: + description: UUID of the session that owns the window + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + format: uuid + type: string + - description: id of the browser window, either the windowId (uuid) or targetId (simple string) + example: 7334da2a-91b0-42c5-6156-76a5eba87430 + in: path + name: windowId + required: true + schema: + description: id of the browser window, either the windowId (uuid) or targetId (simple string) + example: 7334da2a-91b0-42c5-6156-76a5eba87430 + type: string + - description: Renders a navigation bar as part of the live view session + explode: false + in: query + name: includeNavigationBar + schema: + description: Renders a navigation bar as part of the live view session + type: boolean + - description: By default browsers will adapt its resolution to the live view window size. This flag disables that behavior + explode: false + in: query + name: disableResize + schema: + description: By default browsers will adapt its resolution to the live view window size. This flag disables that behavior + type: boolean + - description: "Ex: Screen resolution of the live view in format wxh, e.g. 1920x1080. When this value is provided the live view won't adapt it's resolution to the live view window size" + example: 1920x1080 + explode: false + in: query + name: Screen resolution + schema: + description: "Ex: Screen resolution of the live view in format wxh, e.g. 1920x1080. When this value is provided the live view won't adapt it's resolution to the live view window size" + example: 1920x1080 + type: string + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/WindowEnvelopeDefaultMetaWrapper" + description: Window info + description: Created + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Get information about a browser window in a session + tags: + - Windows + x-fern-audiences: + - public + /sessions/{sessionId}/windows/{windowId}/prompt-content: + post: + operationId: prompt-content + parameters: + - description: The session id to submit a prompt about + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + in: path + name: sessionId + required: true + schema: + description: The session id to submit a prompt about + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + format: uuid + type: string + - description: The window id to submit a prompt about + example: 0334da2a-91b0-42c5-6156-76a5eba87430 + in: path + name: windowId + required: true + schema: + description: The window id to submit a prompt about + example: 0334da2a-91b0-42c5-6156-76a5eba87430 + format: uuid + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Prompt-contentRequest" + required: true + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/ModelResponseExternalSessionAiResponseMetadataWrapper" + description: Created + headers: + content-type: + schema: + type: string + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Submit a prompt about the content in a specific browser window. + tags: + - Windows + x-fern-audiences: + - public + /sessions/{sessionId}/windows/{windowId}/scrape-content: + get: + operationId: scrape-content + parameters: + - description: The session id to scrape + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + in: path + name: sessionId + required: true + schema: + description: The session id to scrape + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + format: uuid + type: string + - description: The window id to scrape + example: 0334da2a-91b0-42c5-6156-76a5eba87430 + in: path + name: windowId + required: true + schema: + description: The window id to scrape + example: 0334da2a-91b0-42c5-6156-76a5eba87430 + format: uuid + type: string + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/ScrapeModelResponseExternalSessionAiResponseMetadataWrapper" + description: Created + headers: + content-type: + schema: + type: string + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Scrape a window + tags: + - Windows + x-fern-audiences: + - public + /sessions/{sessionId}/windows/{windowId}/summarize-content: + post: + operationId: summarize-content + parameters: + - description: The session id to summarize + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + in: path + name: sessionId + required: true + schema: + description: The session id to summarize + example: 6aac6f73-bd89-4a76-ab32-5a6c422e8b0b + format: uuid + type: string + - description: The window id to summarize + example: 0334da2a-91b0-42c5-6156-76a5eba87430 + in: path + name: windowId + required: true + schema: + description: The window id to summarize + example: 0334da2a-91b0-42c5-6156-76a5eba87430 + format: uuid + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Summarize-contentRequest" + required: true + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/ModelResponseExternalSessionAiResponseMetadataWrapper" + description: Created + headers: + content-type: + schema: + type: string + default: + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" + description: Error + summary: Get a summary of content in a browser window + tags: + - Windows + x-fern-audiences: + - public +servers: + - url: https://api.airtop.ai/v1 diff --git a/packages/cli/openapi-ir-to-fern-tests/src/__test__/switchboard.test.ts b/packages/cli/openapi-ir-to-fern-tests/src/__test__/switchboard.test.ts new file mode 100644 index 00000000000..d33985b47ad --- /dev/null +++ b/packages/cli/openapi-ir-to-fern-tests/src/__test__/switchboard.test.ts @@ -0,0 +1,5 @@ +import { testConvertOpenAPI } from "../testConvertOpenApi"; + +describe("open api parser", () => { + testConvertOpenAPI("switchboard", "openapi.yml"); +}); diff --git a/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/convertResponse.ts b/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/convertResponse.ts index 644fe6de542..76e5e8d6854 100644 --- a/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/convertResponse.ts +++ b/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/convertResponse.ts @@ -9,7 +9,11 @@ import { AbstractOpenAPIV3ParserContext } from "../../AbstractOpenAPIV3ParserCon import { FernOpenAPIExtension } from "../../extensions/fernExtensions"; import { OperationContext } from "../contexts"; import { ERROR_NAMES_BY_STATUS_CODE } from "../convertToHttpError"; -import { getApplicationJsonSchemaMediaObject, getSchemaMediaObject } from "./getApplicationJsonSchema"; +import { + getApplicationJsonSchemaMediaObject, + getSchemaMediaObject, + getTextEventStreamObject +} from "./getApplicationJsonSchema"; // The converter will attempt to get response in priority order // (i.e. try for 200, then 201, then 202...) @@ -142,6 +146,44 @@ function convertResolvedResponse({ } } + const textEventStreamObject = getTextEventStreamObject(resolvedResponse.content ?? {}, context); + if (textEventStreamObject != null && streamFormat != null) { + switch (streamFormat) { + case "json": + return ResponseWithExample.streamingJson({ + description: resolvedResponse.description, + responseProperty: undefined, + schema: convertSchemaWithExampleToSchema( + convertSchema( + textEventStreamObject.schema, + false, + context, + responseBreadcrumbs, + source, + namespace + ) + ), + source + }); + case "sse": + return ResponseWithExample.streamingSse({ + description: resolvedResponse.description, + responseProperty: undefined, + schema: convertSchemaWithExampleToSchema( + convertSchema( + textEventStreamObject.schema, + false, + context, + responseBreadcrumbs, + source, + namespace + ) + ), + source + }); + } + } + const jsonMediaObject = getApplicationJsonSchemaMediaObject(resolvedResponse.content ?? {}, context); if (jsonMediaObject != null) { if (streamFormat != null) { diff --git a/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/getApplicationJsonSchema.ts b/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/getApplicationJsonSchema.ts index 4a6a805868a..7c64b19466d 100644 --- a/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/getApplicationJsonSchema.ts +++ b/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/getApplicationJsonSchema.ts @@ -5,6 +5,36 @@ import { isReferenceObject } from "../../../../schema/utils/isReferenceObject"; import { AbstractOpenAPIV3ParserContext } from "../../AbstractOpenAPIV3ParserContext"; import { OpenAPIExtension } from "../../extensions/extensions"; +export interface TextEventStreamObject { + contentType?: string; + schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject; + examples: NamedFullExample[]; +} + +export function getTextEventStreamObject( + media: Record, + context: AbstractOpenAPIV3ParserContext +): TextEventStreamObject | undefined { + for (const contentType of Object.keys(media)) { + // See swagger.io/docs/specification/media-types for reference on "*/*" + if (contentType.includes("text/event-stream")) { + const mediaObject = media[contentType]; + if (mediaObject == null) { + continue; + } + const schema = mediaObject.schema; + + return { + contentType, + schema: schema ?? {}, + examples: getExamples(mediaObject, context) + }; + } + } + + return undefined; +} + export interface ApplicationJsonMediaObject { contentType?: string; schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject; diff --git a/packages/cli/yaml/docs-validator/src/docsAst/visitDocsConfigFileAst.ts b/packages/cli/yaml/docs-validator/src/docsAst/visitDocsConfigFileAst.ts deleted file mode 100644 index c8f93fafe08..00000000000 --- a/packages/cli/yaml/docs-validator/src/docsAst/visitDocsConfigFileAst.ts +++ /dev/null @@ -1,471 +0,0 @@ -import { docsYml } from "@fern-api/configuration"; -import { parseImagePaths, replaceReferencedCode, replaceReferencedMarkdown } from "@fern-api/docs-markdown-utils"; -import { AbsoluteFilePath, dirname, doesPathExist, RelativeFilePath, resolve } from "@fern-api/fs-utils"; -import { TaskContext } from "@fern-api/task-context"; -import { NodePath } from "@fern-api/fern-definition-schema"; -import { readFile } from "fs/promises"; -import yaml from "js-yaml"; -import path from "path"; -import { APIWorkspaceLoader } from "./APIWorkspaceLoader"; -import { DocsConfigFileAstVisitor } from "./DocsConfigFileAstVisitor"; -import { validateVersionConfigFileSchema } from "./validateVersionConfig"; - -export async function visitDocsConfigFileYamlAst( - contents: docsYml.RawSchemas.DocsConfiguration, - visitor: Partial, - absoluteFilepathToConfiguration: AbsoluteFilePath, - absolutePathToFernFolder: AbsoluteFilePath, - context: TaskContext, - loadAPIWorkspace: APIWorkspaceLoader -): Promise { - await visitor.file?.( - { - config: contents - }, - [] - ); - - // the following code parses markdown files for media and adds them to the filepath visitor - let pageEntries: Record = {}; - - try { - // wrap the parse call in a try/catch because it will throw if a markdown file doesn't exist - const { pages } = await docsYml.parseDocsConfiguration({ - rawDocsConfiguration: contents, - context, - absoluteFilepathToDocsConfig: absoluteFilepathToConfiguration, - absolutePathToFernFolder - }); - pageEntries = pages; - } catch { - // if the parse fails, we'll just skip this step - } - - // replaces all instances of with the content of the referenced markdown file - // this should happen before we parse image paths, as the referenced markdown files may contain images. - for (const [relativePath, markdown] of Object.entries(pageEntries)) { - pageEntries[RelativeFilePath.of(relativePath)] = await replaceReferencedMarkdown({ - markdown, - absolutePathToFernFolder, - absolutePathToMdx: resolve(absolutePathToFernFolder, relativePath), - context - }); - pageEntries[RelativeFilePath.of(relativePath)] = await replaceReferencedCode({ - markdown, - absolutePathToFernFolder, - absolutePathToMdx: resolve(absolutePathToFernFolder, relativePath), - context - }); - } - - for (const [relativePath, markdown] of Object.entries(pageEntries)) { - const { filepaths } = parseImagePaths(markdown, { - absolutePathToFernFolder, - absolutePathToMdx: resolve(absolutePathToFernFolder, relativePath) - }); - - // visit each media filepath in each markdown file - for (const filepath of filepaths) { - await visitor.filepath?.( - { - absoluteFilepath: filepath, - value: path.relative(absolutePathToFernFolder, filepath), - willBeUploaded: true - }, - [relativePath] - ); - } - } - - if (contents.js != null) { - if (Array.isArray(contents.js)) { - // multiple JS configs - await Promise.all( - contents.js.map((script, idx) => - visitScript({ - absoluteFilepathToConfiguration, - visitor, - script, - nodePath: ["js", `${idx}`] - }) - ) - ); - } else { - // single JS config - await visitScript({ - absoluteFilepathToConfiguration, - visitor, - script: contents.js, - nodePath: ["js"] - }); - } - } - - if (contents.css != null) { - if (Array.isArray(contents.css)) { - // multiple CSS files - await Promise.all( - contents.css.map((stylesheet, idx) => - visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: stylesheet, - visitor, - nodePath: ["css", `${idx}`], - willBeUploaded: false - }) - ) - ); - } else { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.css, - visitor, - nodePath: ["css"], - willBeUploaded: false - }); - } - } - - if (contents.backgroundImage != null) { - if (typeof contents.backgroundImage === "string") { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.backgroundImage, - visitor, - nodePath: ["background-image"] - }); - } else { - if (contents.backgroundImage.dark != null) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.backgroundImage.dark, - visitor, - nodePath: ["background-image", "dark"] - }); - } - if (contents.backgroundImage.light != null) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.backgroundImage.light, - visitor, - nodePath: ["background-image", "light"] - }); - } - } - } - if (contents.favicon != null) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.favicon, - visitor, - nodePath: ["favicon"] - }); - } - if (contents.logo?.dark != null) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.logo.dark, - visitor, - nodePath: ["logo", "dark"] - }); - } - if (contents.logo?.light != null) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.logo.light, - visitor, - nodePath: ["logo", "light"] - }); - } - - if (contents.navigation != null) { - await visitNavigation({ - navigation: contents.navigation, - visitor, - nodePath: ["navigation"], - absoluteFilepathToConfiguration, - loadAPIWorkspace, - context - }); - } - - if (contents.typography?.codeFont?.path != null) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.typography.codeFont.path, - visitor, - nodePath: ["typography", "codeFont"] - }); - } - - for (const path of contents.typography?.codeFont?.paths ?? []) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: typeof path === "string" ? path : path.path, - visitor, - nodePath: ["typography", "codeFont"] - }); - } - - if (contents.typography?.bodyFont?.path != null) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.typography.bodyFont.path, - visitor, - nodePath: ["typography", "bodyFont"] - }); - } - - for (const path of contents.typography?.bodyFont?.paths ?? []) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: typeof path === "string" ? path : path.path, - visitor, - nodePath: ["typography", "bodyFont"] - }); - } - - if (contents.typography?.headingsFont?.path != null) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: contents.typography.headingsFont.path, - visitor, - nodePath: ["typography", "headingsFont"] - }); - } - - for (const path of contents.typography?.headingsFont?.paths ?? []) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: typeof path === "string" ? path : path.path, - visitor, - nodePath: ["typography", "headingsFont"] - }); - } - - if (contents.versions != null) { - await Promise.all( - contents.versions.map(async (version, idx) => { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: version.path, - visitor, - nodePath: ["versions", `${idx}`], - willBeUploaded: false - }); - const absoluteFilepath = resolve(dirname(absoluteFilepathToConfiguration), version.path); - const content = yaml.load((await readFile(absoluteFilepath)).toString()); - if (await doesPathExist(absoluteFilepath)) { - await visitor.versionFile?.( - { - path: version.path, - content - }, - [version.path] - ); - } - const parsedVersionFile = await validateVersionConfigFileSchema({ value: content }); - if (parsedVersionFile.type === "success") { - await visitNavigation({ - navigation: parsedVersionFile.contents.navigation, - visitor, - nodePath: ["navigation"], - absoluteFilepathToConfiguration: absoluteFilepath, - loadAPIWorkspace, - context - }); - } - }) - ); - } -} - -async function visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath, - visitor, - nodePath, - willBeUploaded = true -}: { - absoluteFilepathToConfiguration: AbsoluteFilePath; - rawUnresolvedFilepath: string; - visitor: Partial; - nodePath: NodePath; - willBeUploaded?: boolean; -}) { - const absoluteFilepath = resolve(dirname(absoluteFilepathToConfiguration), rawUnresolvedFilepath); - await visitor.filepath?.( - { - absoluteFilepath, - value: rawUnresolvedFilepath, - willBeUploaded - }, - nodePath - ); -} - -async function visitNavigation({ - navigation, - visitor, - nodePath, - absoluteFilepathToConfiguration, - loadAPIWorkspace, - context -}: { - navigation: docsYml.RawSchemas.NavigationConfig; - visitor: Partial; - nodePath: NodePath; - absoluteFilepathToConfiguration: AbsoluteFilePath; - loadAPIWorkspace: APIWorkspaceLoader; - context: TaskContext; -}): Promise { - if (navigationConfigIsTabbed(navigation)) { - await Promise.all( - navigation.map(async (tab, tabIdx) => { - if (tab.layout != null) { - await Promise.all( - tab.layout.map(async (item, itemIdx) => { - await visitNavigationItem({ - navigationItem: item, - visitor, - nodePath: [...nodePath, `${tabIdx}`, "layout", `${itemIdx}`], - absoluteFilepathToConfiguration, - loadAPIWorkspace, - context - }); - }) - ); - } - }) - ); - } else { - await Promise.all( - navigation.map(async (item, itemIdx) => { - await visitNavigationItem({ - navigationItem: item, - visitor, - nodePath: [...nodePath, `${itemIdx}`], - absoluteFilepathToConfiguration, - loadAPIWorkspace, - context - }); - }) - ); - } -} - -async function visitNavigationItem({ - navigationItem, - visitor, - nodePath, - absoluteFilepathToConfiguration, - loadAPIWorkspace, - context -}: { - navigationItem: docsYml.RawSchemas.NavigationItem; - visitor: Partial; - nodePath: NodePath; - absoluteFilepathToConfiguration: AbsoluteFilePath; - loadAPIWorkspace: APIWorkspaceLoader; - context: TaskContext; -}): Promise { - if (navigationItemIsPage(navigationItem)) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath: navigationItem.path, - visitor, - nodePath: [...nodePath, "page"], - willBeUploaded: false - }); - const absoluteFilepath = resolve(dirname(absoluteFilepathToConfiguration), navigationItem.path); - if (await doesPathExist(absoluteFilepath)) { - const content = (await readFile(absoluteFilepath)).toString(); - await visitor.markdownPage?.( - { - title: navigationItem.page, - content, - absoluteFilepath - }, - [...nodePath, "page", navigationItem.path] - ); - } - } - - if (navigationItemIsSection(navigationItem)) { - await Promise.all( - navigationItem.contents.map(async (item, itemIdx) => { - await visitNavigationItem({ - navigationItem: item, - visitor, - nodePath: [...nodePath, "section", "contents", `${itemIdx}`], - absoluteFilepathToConfiguration, - loadAPIWorkspace, - context - }); - }) - ); - } - - if (navigationItemIsApi(navigationItem)) { - const workspace = loadAPIWorkspace(navigationItem.apiName != null ? navigationItem.apiName : undefined); - if (workspace != null) { - await visitor.apiSection?.( - { - config: navigationItem, - workspace, - context - }, - [...nodePath, "api"] - ); - } - } -} - -async function visitScript({ - absoluteFilepathToConfiguration, - visitor, - script, - nodePath -}: { - absoluteFilepathToConfiguration: AbsoluteFilePath; - visitor: Partial; - script: docsYml.RawSchemas.JsConfigOptions; - nodePath: NodePath; -}) { - const rawUnresolvedFilepath = typeof script === "string" ? script : "path" in script ? script.path : null; - - if (rawUnresolvedFilepath) { - await visitFilepath({ - absoluteFilepathToConfiguration, - rawUnresolvedFilepath, - visitor, - nodePath, - willBeUploaded: true - }); - } -} - -function navigationItemIsPage(item: docsYml.RawSchemas.NavigationItem): item is docsYml.RawSchemas.PageConfiguration { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - return (item as docsYml.RawSchemas.PageConfiguration).page != null; -} - -function navigationItemIsSection( - item: docsYml.RawSchemas.NavigationItem -): item is docsYml.RawSchemas.SectionConfiguration { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - return (item as docsYml.RawSchemas.SectionConfiguration).section != null; -} - -function navigationItemIsApi( - item: docsYml.RawSchemas.NavigationItem -): item is docsYml.RawSchemas.ApiReferenceConfiguration { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - return (item as docsYml.RawSchemas.ApiReferenceConfiguration).api != null; -} - -function navigationConfigIsTabbed( - config: docsYml.RawSchemas.NavigationConfig -): config is docsYml.RawSchemas.TabbedNavigationConfig { - return (config as docsYml.RawSchemas.TabbedNavigationConfig)[0]?.tab != null; -} diff --git a/packages/cli/yaml/docs-validator/src/docsAst/visitDocsConfigFileYamlAst.ts b/packages/cli/yaml/docs-validator/src/docsAst/visitDocsConfigFileYamlAst.ts new file mode 100644 index 00000000000..25f803002e4 --- /dev/null +++ b/packages/cli/yaml/docs-validator/src/docsAst/visitDocsConfigFileYamlAst.ts @@ -0,0 +1,327 @@ +import { docsYml } from "@fern-api/configuration"; +import { AbsoluteFilePath, dirname, doesPathExist, resolve } from "@fern-api/fs-utils"; +import { TaskContext } from "@fern-api/task-context"; +import yaml from "js-yaml"; +import { DocsConfigFileAstVisitor } from "./DocsConfigFileAstVisitor"; +import { APIWorkspaceLoader } from "./APIWorkspaceLoader"; +import { noop, visitObject } from "@fern-api/core-utils"; +import { NodePath } from "@fern-api/fern-definition-schema"; +import { visitFilepath } from "./visitFilepath"; +import { visitNavigationAst } from "./visitNavigationAst"; +import { validateVersionConfigFileSchema } from "./validateVersionConfig"; +import { readFile } from "fs/promises"; + +export declare namespace visitDocsConfigFileYamlAst { + interface Args { + contents: docsYml.RawSchemas.DocsConfiguration; + visitor: Partial; + absoluteFilepathToConfiguration: AbsoluteFilePath; + absolutePathToFernFolder: AbsoluteFilePath; + context: TaskContext; + loadAPIWorkspace: APIWorkspaceLoader; + } +} + +export async function visitDocsConfigFileYamlAst({ + contents, + visitor, + absoluteFilepathToConfiguration, + context, + loadAPIWorkspace, + absolutePathToFernFolder +}: visitDocsConfigFileYamlAst.Args): Promise { + await visitor.file?.( + { + config: contents + }, + [] + ); + await visitObject(contents, { + instances: noop, + analytics: noop, + announcement: noop, + backgroundImage: async (background) => { + if (background == null) { + return; + } else if (typeof background === "string") { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: background, + visitor, + nodePath: ["background-image"] + }); + } else { + if (background.dark != null) { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: background.dark, + visitor, + nodePath: ["background-image", "dark"] + }); + } + if (background.light != null) { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: background.light, + visitor, + nodePath: ["background-image", "light"] + }); + } + } + }, + colors: noop, + css: async (css) => { + if (css == null) { + return; + } else if (Array.isArray(css)) { + // multiple CSS files + await Promise.all( + css.map((stylesheet, idx) => + visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: stylesheet, + visitor, + nodePath: ["css", `${idx}`], + willBeUploaded: false + }) + ) + ); + } else { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: css, + visitor, + nodePath: ["css"], + willBeUploaded: false + }); + } + }, + defaultLanguage: noop, + experimental: noop, + favicon: async (favicon) => { + if (favicon == null) { + return; + } + + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: favicon, + visitor, + nodePath: ["favicon"] + }); + }, + footerLinks: noop, + integrations: noop, + js: async (js) => { + if (js == null) { + return; + } else if (Array.isArray(js)) { + // multiple JS configs + await Promise.all( + js.map((script, idx) => + visitScript({ + absoluteFilepathToConfiguration, + visitor, + script, + nodePath: ["js", `${idx}`] + }) + ) + ); + } else { + // single JS config + await visitScript({ + absoluteFilepathToConfiguration, + visitor, + script: js, + nodePath: ["js"] + }); + } + }, + landingPage: noop, + layout: noop, + logo: async (logo) => { + if (contents.logo?.dark != null) { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: contents.logo.dark, + visitor, + nodePath: ["logo", "dark"] + }); + } + if (contents.logo?.light != null) { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: contents.logo.light, + visitor, + nodePath: ["logo", "light"] + }); + } + }, + metadata: noop, + navbarLinks: noop, + navigation: async (navigation) => { + if (navigation == null) { + return; + } + + await visitNavigationAst({ + absolutePathToFernFolder, + navigation, + visitor, + nodePath: ["navigation"], + absoluteFilepathToConfiguration, + loadAPIWorkspace, + context + }); + }, + redirects: noop, + tabs: noop, + title: noop, + typography: async (typography) => { + if (typography == null) { + return; + } + await visitObject(typography, { + bodyFont: async (body) => { + if (body == null) { + return; + } + await visitFontConfig({ + absoluteFilepathToConfiguration, + visitor, + font: body, + nodePath: ["typography", "bodyFont"] + }); + }, + codeFont: async (code) => { + if (code == null) { + return; + } + await visitFontConfig({ + absoluteFilepathToConfiguration, + visitor, + font: code, + nodePath: ["typography", "codeFont"] + }); + }, + headingsFont: async (headings) => { + if (headings == null) { + return; + } + await visitFontConfig({ + absoluteFilepathToConfiguration, + visitor, + font: headings, + nodePath: ["typography", "headingsFont"] + }); + } + }); + }, + versions: async (versions) => { + if (versions == null) { + return; + } + + await Promise.all( + versions.map(async (version, idx) => { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: version.path, + visitor, + nodePath: ["versions", `${idx}`], + willBeUploaded: false + }); + const absoluteFilepath = resolve(dirname(absoluteFilepathToConfiguration), version.path); + const content = yaml.load((await readFile(absoluteFilepath)).toString()); + if (await doesPathExist(absoluteFilepath)) { + await visitor.versionFile?.( + { + path: version.path, + content + }, + [version.path] + ); + } + const parsedVersionFile = await validateVersionConfigFileSchema({ value: content }); + if (parsedVersionFile.type === "success") { + await visitNavigationAst({ + absolutePathToFernFolder, + navigation: parsedVersionFile.contents.navigation, + visitor, + nodePath: ["navigation"], + absoluteFilepathToConfiguration: absoluteFilepath, + loadAPIWorkspace, + context + }); + } + }) + ); + } + }); +} + +async function visitFontConfig({ + absoluteFilepathToConfiguration, + visitor, + font, + nodePath +}: { + absoluteFilepathToConfiguration: AbsoluteFilePath; + visitor: Partial; + font: docsYml.RawSchemas.FontConfig; + nodePath: NodePath; +}): Promise { + if (font.path != null) { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: font.path, + visitor, + nodePath, + willBeUploaded: true + }); + } + + for (const path of font.paths ?? []) { + if (typeof path === "string") { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: path, + visitor, + nodePath, + willBeUploaded: true + }); + } else { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: path.path, + visitor, + nodePath, + willBeUploaded: true + }); + } + } +} + +async function visitScript({ + absoluteFilepathToConfiguration, + visitor, + script, + nodePath +}: { + absoluteFilepathToConfiguration: AbsoluteFilePath; + visitor: Partial; + script: docsYml.RawSchemas.JsConfigOptions; + nodePath: NodePath; +}) { + const rawUnresolvedFilepath = typeof script === "string" ? script : "path" in script ? script.path : null; + + if (rawUnresolvedFilepath) { + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath, + visitor, + nodePath, + willBeUploaded: true + }); + } +} diff --git a/packages/cli/yaml/docs-validator/src/docsAst/visitFilepath.ts b/packages/cli/yaml/docs-validator/src/docsAst/visitFilepath.ts new file mode 100644 index 00000000000..42717c2a4cf --- /dev/null +++ b/packages/cli/yaml/docs-validator/src/docsAst/visitFilepath.ts @@ -0,0 +1,27 @@ +import { NodePath } from "@fern-api/fern-definition-schema"; +import { AbsoluteFilePath, dirname, resolve } from "@fern-api/fs-utils"; +import { DocsConfigFileAstVisitor } from "./DocsConfigFileAstVisitor"; + +export async function visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath, + visitor, + nodePath, + willBeUploaded = true +}: { + absoluteFilepathToConfiguration: AbsoluteFilePath; + rawUnresolvedFilepath: string; + visitor: Partial; + nodePath: NodePath; + willBeUploaded?: boolean; +}): Promise { + const absoluteFilepath = resolve(dirname(absoluteFilepathToConfiguration), rawUnresolvedFilepath); + await visitor.filepath?.( + { + absoluteFilepath, + value: rawUnresolvedFilepath, + willBeUploaded + }, + nodePath + ); +} diff --git a/packages/cli/yaml/docs-validator/src/docsAst/visitNavigationAst.ts b/packages/cli/yaml/docs-validator/src/docsAst/visitNavigationAst.ts new file mode 100644 index 00000000000..5681048f593 --- /dev/null +++ b/packages/cli/yaml/docs-validator/src/docsAst/visitNavigationAst.ts @@ -0,0 +1,200 @@ +import { docsYml } from "@fern-api/configuration"; +import { NodePath } from "@fern-api/fern-definition-schema"; +import { AbsoluteFilePath, dirname, doesPathExist, relative, resolve } from "@fern-api/fs-utils"; +import { APIWorkspaceLoader } from "./APIWorkspaceLoader"; +import { DocsConfigFileAstVisitor } from "./DocsConfigFileAstVisitor"; +import { visitFilepath } from "./visitFilepath"; +import { readFile } from "fs/promises"; +import { visitObject, noop } from "@fern-api/core-utils"; +import { TaskContext } from "@fern-api/task-context"; +import { parseImagePaths } from "@fern-api/docs-markdown-utils"; + +export declare namespace visitNavigationAst { + interface Args { + absolutePathToFernFolder: AbsoluteFilePath; + navigation: docsYml.RawSchemas.NavigationConfig; + visitor: Partial; + nodePath: NodePath; + absoluteFilepathToConfiguration: AbsoluteFilePath; + loadAPIWorkspace: APIWorkspaceLoader; + context: TaskContext; + } +} + +export async function visitNavigationAst({ + absolutePathToFernFolder, + navigation, + loadAPIWorkspace, + visitor, + absoluteFilepathToConfiguration, + context, + nodePath +}: visitNavigationAst.Args): Promise { + if (navigationConfigIsTabbed(navigation)) { + await Promise.all( + navigation.map(async (tab, tabIdx) => { + if (tab.layout != null) { + await Promise.all( + tab.layout.map(async (item, itemIdx) => { + await visitNavigationItem({ + absolutePathToFernFolder, + navigationItem: item, + visitor, + nodePath: [...nodePath, `${tabIdx}`, "layout", `${itemIdx}`], + absoluteFilepathToConfiguration, + loadAPIWorkspace, + context + }); + }) + ); + } + }) + ); + } else { + await Promise.all( + navigation.map(async (item, itemIdx) => { + await visitNavigationItem({ + absolutePathToFernFolder, + navigationItem: item, + visitor, + nodePath: [...nodePath, `${itemIdx}`], + absoluteFilepathToConfiguration, + loadAPIWorkspace, + context + }); + }) + ); + } +} +async function visitNavigationItem({ + absolutePathToFernFolder, + navigationItem, + visitor, + nodePath, + absoluteFilepathToConfiguration, + loadAPIWorkspace, + context +}: { + absolutePathToFernFolder: AbsoluteFilePath; + navigationItem: docsYml.RawSchemas.NavigationItem; + visitor: Partial; + nodePath: NodePath; + absoluteFilepathToConfiguration: AbsoluteFilePath; + loadAPIWorkspace: APIWorkspaceLoader; + context: TaskContext; +}): Promise { + await visitObject(navigationItem, { + alphabetized: noop, + api: noop, + apiName: noop, + audiences: noop, + displayErrors: noop, + snippets: noop, + summary: noop, + title: noop, + layout: noop, + icon: noop, + slug: noop, + hidden: noop, + skipSlug: noop, + paginated: noop, + playground: noop, + flattened: noop, + path: async (path: string | undefined): Promise => { + if (path == null) { + return; + } + + await visitFilepath({ + absoluteFilepathToConfiguration, + rawUnresolvedFilepath: path, + visitor, + nodePath: [...nodePath, "path"], + willBeUploaded: false + }); + }, + page: noop, + contents: async (items: docsYml.RawSchemas.NavigationItem[] | undefined): Promise => { + if (items == null) { + return; + } + items.map(async (item, idx) => { + await visitNavigationItem({ + absolutePathToFernFolder, + navigationItem: item, + visitor, + nodePath: [...nodePath, "contents", `${idx}`], + absoluteFilepathToConfiguration, + loadAPIWorkspace, + context + }); + }); + } + }); + + if (navigationItemIsPage(navigationItem)) { + const absoluteFilepath = resolve(dirname(absoluteFilepathToConfiguration), navigationItem.path); + if (await doesPathExist(absoluteFilepath)) { + const content = (await readFile(absoluteFilepath)).toString(); + await visitor.markdownPage?.( + { + title: navigationItem.page, + content, + absoluteFilepath + }, + [...nodePath, navigationItem.path] + ); + + try { + const { filepaths } = parseImagePaths(content, { + absolutePathToFernFolder, + absolutePathToMdx: absoluteFilepath + }); + + // visit each media filepath in each markdown file + for (const filepath of filepaths) { + await visitor.filepath?.( + { + absoluteFilepath: filepath, + value: relative(absolutePathToFernFolder, filepath), + willBeUploaded: true + }, + [...nodePath, navigationItem.path] + ); + } + } catch (err) {} + } + } + + if (navigationItemIsApi(navigationItem)) { + const workspace = loadAPIWorkspace(navigationItem.apiName != null ? navigationItem.apiName : undefined); + if (workspace != null) { + await visitor.apiSection?.( + { + config: navigationItem, + workspace, + context + }, + [...nodePath, "api"] + ); + } + } +} + +function navigationItemIsPage(item: docsYml.RawSchemas.NavigationItem): item is docsYml.RawSchemas.PageConfiguration { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return (item as docsYml.RawSchemas.PageConfiguration).page != null; +} + +function navigationItemIsApi( + item: docsYml.RawSchemas.NavigationItem +): item is docsYml.RawSchemas.ApiReferenceConfiguration { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return (item as docsYml.RawSchemas.ApiReferenceConfiguration).api != null; +} + +function navigationConfigIsTabbed( + config: docsYml.RawSchemas.NavigationConfig +): config is docsYml.RawSchemas.TabbedNavigationConfig { + return (config as docsYml.RawSchemas.TabbedNavigationConfig)[0]?.tab != null; +} diff --git a/packages/cli/yaml/docs-validator/src/index.ts b/packages/cli/yaml/docs-validator/src/index.ts index bbf51bdce26..4e953ce7d81 100644 --- a/packages/cli/yaml/docs-validator/src/index.ts +++ b/packages/cli/yaml/docs-validator/src/index.ts @@ -7,5 +7,5 @@ export { validateVersionConfigFileSchema } from "./docsAst/validateVersionConfig export { getReferencedMarkdownFiles } from "./rules/valid-markdown-link/valid-markdown-link"; export { FrontmatterSchema } from "./rules/valid-markdown/valid-markdown"; export { validateDocsWorkspace } from "./validateDocsWorkspace"; -export { visitDocsConfigFileYamlAst } from "./docsAst/visitDocsConfigFileAst"; +export { visitDocsConfigFileYamlAst } from "./docsAst/visitDocsConfigFileYamlAst"; export { type APIWorkspaceLoader } from "./docsAst/APIWorkspaceLoader"; diff --git a/packages/cli/yaml/docs-validator/src/rules/valid-markdown/valid-markdown.ts b/packages/cli/yaml/docs-validator/src/rules/valid-markdown/valid-markdown.ts index 1f86ec64233..356da9c68b6 100644 --- a/packages/cli/yaml/docs-validator/src/rules/valid-markdown/valid-markdown.ts +++ b/packages/cli/yaml/docs-validator/src/rules/valid-markdown/valid-markdown.ts @@ -4,6 +4,7 @@ import remarkMath from "remark-math"; import rehypeKatex from "rehype-katex"; import { z } from "zod"; import { Rule } from "../../Rule"; +import { parseMarkdownToTree } from "@fern-api/docs-markdown-utils"; export const ValidMarkdownRule: Rule = { name: "valid-markdown", @@ -64,6 +65,8 @@ export const FrontmatterSchema = z.object({ async function parseMarkdown({ markdown }: { markdown: string }): Promise { try { + parseMarkdownToTree(markdown); + const parsed = await serialize(markdown, { scope: {}, mdxOptions: { diff --git a/packages/cli/yaml/docs-validator/src/validateDocsWorkspace.ts b/packages/cli/yaml/docs-validator/src/validateDocsWorkspace.ts index bdf76261d4e..18917c58cff 100644 --- a/packages/cli/yaml/docs-validator/src/validateDocsWorkspace.ts +++ b/packages/cli/yaml/docs-validator/src/validateDocsWorkspace.ts @@ -4,7 +4,7 @@ import { TaskContext } from "@fern-api/task-context"; import { DocsWorkspace } from "@fern-api/workspace-loader"; import { createDocsConfigFileAstVisitorForRules } from "./createDocsConfigFileAstVisitorForRules"; import { APIWorkspaceLoader } from "./docsAst/APIWorkspaceLoader"; -import { visitDocsConfigFileYamlAst } from "./docsAst/visitDocsConfigFileAst"; +import { visitDocsConfigFileYamlAst } from "./docsAst/visitDocsConfigFileYamlAst"; import { getAllRules } from "./getAllRules"; import { Rule } from "./Rule"; import { ValidationViolation } from "./ValidationViolation"; @@ -41,14 +41,17 @@ export async function runRulesOnDocsWorkspace({ } }); - await visitDocsConfigFileYamlAst( - workspace.config, - astVisitor, - join(workspace.absoluteFilePath, RelativeFilePath.of(DOCS_CONFIGURATION_FILENAME)), - workspace.absoluteFilePath, + await visitDocsConfigFileYamlAst({ + contents: workspace.config, + visitor: astVisitor, + absoluteFilepathToConfiguration: join( + workspace.absoluteFilePath, + RelativeFilePath.of(DOCS_CONFIGURATION_FILENAME) + ), + absolutePathToFernFolder: workspace.absoluteFilePath, context, - loadApiWorkspace - ); + loadAPIWorkspace: loadApiWorkspace + }); return violations; } diff --git a/packages/commons/core-utils/package.json b/packages/commons/core-utils/package.json index 0431e3db877..e72175ede11 100644 --- a/packages/commons/core-utils/package.json +++ b/packages/commons/core-utils/package.json @@ -30,9 +30,11 @@ "strip-ansi": "^7.1.0", "ua-parser-js": "^1.0.37", "whatwg-mimetype": "^4.0.0", - "lodash-es": "^4.17.21" + "lodash-es": "^4.17.21", + "title": "^3.5.3" }, "devDependencies": { + "@types/title": "^3.4.3", "@types/lodash-es": "^4.17.12", "@types/lodash": "^4.17.4", "@types/node": "^18.7.18", diff --git a/packages/commons/core-utils/src/ObjectPropertiesVisitor.ts b/packages/commons/core-utils/src/ObjectPropertiesVisitor.ts index a86075adc78..5ec5d19bd20 100644 --- a/packages/commons/core-utils/src/ObjectPropertiesVisitor.ts +++ b/packages/commons/core-utils/src/ObjectPropertiesVisitor.ts @@ -4,7 +4,7 @@ export type ObjectPropertiesVisitor = { [K in keyof T]-?: (value: T[K]) => R; }; -export async function visitObject>( +export async function visitObject( object: T, visitor: ObjectPropertiesVisitor> ): Promise { diff --git a/packages/commons/core-utils/src/index.ts b/packages/commons/core-utils/src/index.ts index 7878922f092..0297972c6b4 100644 --- a/packages/commons/core-utils/src/index.ts +++ b/packages/commons/core-utils/src/index.ts @@ -22,3 +22,5 @@ export type { Digit, Letter, LowercaseLetter, UppercaseLetter } from "./types"; export { visitDiscriminatedUnion } from "./visitDiscriminatedUnion"; export type { WithoutQuestionMarks } from "./withoutQuestionMarks"; export { mergeWithOverrides } from "./mergeWithOverrides"; +export { titleCase } from "./titleCase"; +export { stripLeadingSlash } from "./stripLeadingSlash"; diff --git a/packages/commons/core-utils/src/stripLeadingSlash.ts b/packages/commons/core-utils/src/stripLeadingSlash.ts new file mode 100644 index 00000000000..1ac5f11b92a --- /dev/null +++ b/packages/commons/core-utils/src/stripLeadingSlash.ts @@ -0,0 +1,5 @@ +export function stripLeadingSlash(str: string): string; +export function stripLeadingSlash(str: string | undefined): string | undefined; +export function stripLeadingSlash(str: string | undefined): string | undefined { + return str?.replace(/^\/+/, ""); +} diff --git a/packages/commons/core-utils/src/titleCase.ts b/packages/commons/core-utils/src/titleCase.ts new file mode 100644 index 00000000000..43bb9031f52 --- /dev/null +++ b/packages/commons/core-utils/src/titleCase.ts @@ -0,0 +1,380 @@ +import title from "title"; + +export function titleCase(name: string): string { + // regex match pascalCase or CamelCase and add spaces between words + name = name.replace(/([a-z])([A-Z])/g, "$1 $2"); + + // regex match snake_case and replace "_" with " " + name = name.replace(/_/g, " "); + + // regex match kebab-case and replace "-" with " " + name = name.replace(/-/g, " "); + + const titleCased = title(name, { special: SPECIAL_TOKENS }); + + // regex match "V 2", "V 4", etc. and replace it with "V2", "V4", etc. + const versionedTitle = titleCased.replace(/V\s(\d)/g, "V$1"); + return versionedTitle; +} + +export const SPECIAL_TOKENS = [ + // privacy + "PII", + "PHI", + "PCI", + "GDPR", + "CCPA", + "HIPAA", + "COPPA", + "FERPA", + "GLBA", + "SOX", + "FISMA", + "NIST", + "CIS", + "ISO", + "IEC", + "ITAR", + "EAR", + "CMMC", + "CUI", + "CDI", + "FTC", + "FCC", + "SEC", + "FINRA", + + // security + "XSS", + "CSRF", + "SSRF", + "XSRF", + "TLS", + "SSL", + "SSH", + "API", + "OAuth", + "OAuth1", + "OAuth1.0", + "OAuth2", + "OAuth2.0", + "SAML", + "OpenID", + "OpenID Connect", + "CAPTCHA", + "reCAPTCHA", + "2FA", + "MFA", + "OTP", + "TOTP", + "HOTP", + "U2F", + "FIDO", + "FIDO2", + "PKI", + "HMAC", + "AES", + "RSA", + "SHA", + "MD5", + "BCrypt", + "PBKDF2", + "Argon2", + "SCrypt", + "JWT", + "JWE", + "JWS", + "JWK", + "JWA", + "JOSE", + + // shopping + "SKU", + "SKUs", + "UPC", + "EAN", + "ISBN", + "ASIN", + "MPN", + "MSRP", + "MAP", + "RRP", + "MSRP", + + // time + "AM", + "PM", + "UTC", + "GMT", + "PST", + "PDT", + "EST", + "EDT", + "CST", + "CDT", + "MST", + "MDT", + + // geography + "USA", + "UK", + "EU", + "UAE", + "APAC", + "EMEA", + "LATAM", + "ANZ", + "SEA", + "MEA", + "MENA", + "NATO", + "NA", + "SA", + "CA", + "EU", + "AU", + "NZ", + "JP", + "KR", + "CN", + "HK", + "TW", + "SG", + "MY", + "TH", + "ID", + "PH", + "VN", + "IN", + "PK", + "BD", + "LK", + "NP", + "MM", + "KH", + "LA", + "MM", + "BT", + "MV", + + // finance + "USD", + "EUR", + "GBP", + "JPY", + "CNY", + "RUB", + "INR", + "AUD", + "CAD", + "CHF", + "SGD", + "MYR", + "THB", + "IDR", + "KRW", + "PHP", + "VND", + "HKD", + "TWD", + "MXN", + "BRL", + "ARS", + "CLP", + "COP", + "PEN", + "ZAR", + "NGN", + "EGP", + "AED", + "SAR", + "ILS", + "TRY", + "SEK", + "NOK", + "DKK", + "ISK", + "HUF", + "PLN", + "CZK", + "RON", + "BGN", + + // programming + "API", + "APIs", + "SDK", + "SDKs", + "AI", + "OCR", + "REST", + "SOAP", + "JSON", + "XML", + "HTTP", + "HTTPS", + "URI", + "URL", + "CRUD", + "RESTful", + "KYB", + "KYC", + "AML", + "HTML", + "CSS", + "JS", + "SQL", + "DB", + "UI", + "UX", + "SaaS", + "PaaS", + "IaaS", + "IP", + "TCP", + "UDP", + "DNS", + "FTP", + "SMTP", + "IMAP", + "POP3", + "CSV", + "MVC", + "MVP", + "MVVM", + "DOM", + "SPA", + "SSR", + "CSR", + "DDoS", + "CDN", + "IoT", + "ML", + "DL", + "NLP", + "CLI", + "GUI", + "BI", + "ETL", + "RDBMS", + "NoSQL", + "IDE", + "CMS", + "CCPA", + "POSIX", + "ABI", + "API", + "AST", + "COBOL", + "DDL", + "DML", + + // AI-related + "NN", // Neural Network + "CNN", // Convolutional Neural Network + "RNN", // Recurrent Neural Network + "LSTM", // Long Short Term Memory + "GRU", // Gated Recurrent Unit + "ANN", // Artificial Neural Network + "GAN", // Generative Adversarial Network + "RL", // Reinforcement Learning + "DL", // Deep Learning + "ML", // Machine Learning + "NLP", // Natural Language Processing + "NLG", // Natural Language Generation + "NLU", // Natural Language Understanding + "BERT", // Bidirectional Encoder Representations from Transformers + "GPT", // Generative Pre-training Transformer + "SVM", // Support Vector Machine + "PCA", // Principal Component Analysis + "AI", // Artificial Intelligence + "CV", // Computer Vision + "TF", // TensorFlow + "TTS", // Text-to-Speech + "ASR", // Automatic Speech Recognition + "HMM", // Hidden Markov Model + "DNN", // Deep Neural Network + "MLP", // Multi-Layer Perceptron + "RBM", // Restricted Boltzmann Machine + "CRF", // Conditional Random Field + + // Media + "PDF", + "PDFs", + "RTF", + "TXT", + "XLS", + "XLSX", + "PPT", + + // Image + "JPG", + "JPEG", + "PNG", + "GIF", + "GIFs", + "SVG", + "TIFF", + "BMP", + "ICO", + "PSD", + "WebP", + "AVIF", + "HEIF", + "HEIC", + "EPS", + + // Audio + "MP3", + "WAV", + "AIFF", + "FLAC", + "WMA", + "AAC", + "OGG", + + // Video + "AVI", + "WMV", + "MOV", + "M4V", + "MP4", + "MPG", + "MPEG", + "FLV", + "SWF", + "MKV", + "WebM", + + // Cloud Computing + "GCP", // Google Cloud Platform + "AWS", // Amazon Web Services + "VM", // Virtual Machines + "VPC", // Virtual Private Cloud + "S3", // AWS Simple Storage Service + "EC2", // AWS Elastic Compute Cloud + + // Data Storage and Databases + "DynamoDB", + "CosmosDB", + "BigQuery", + "CI/CD", + + // Security and Compliance + "SOC1", // Service Organization Control 1 + "SOC2", // Service Organization Control 2 + "SOC3", // Service Organization Control 3 + "PCI DSS", // Payment Card Industry Data Security Standard + "WAF", // Web Application Firewall + "IAM", // Identity and Access Management + + // Networking + "SDN", // Software-Defined Networking + "MPLS", // Multi-Protocol Label Switching + "BGP", // Border Gateway Protocol + + // Frameworks and Libraries + "Vue.js", + "Node.js", + ".NET" +]; diff --git a/packages/commons/fs-utils/src/__test__/relativize.test.ts b/packages/commons/fs-utils/src/__test__/relativize.test.ts index b4d7bca4f71..0ab330b3798 100644 --- a/packages/commons/fs-utils/src/__test__/relativize.test.ts +++ b/packages/commons/fs-utils/src/__test__/relativize.test.ts @@ -6,7 +6,7 @@ describe("join", () => { it("simple", async () => { const from = AbsoluteFilePath.of("/path/to/fern"); const to = AbsoluteFilePath.of("/path/to/fern/docs/markdown.md"); - const path = await relativize(from, to); + const path = relativize(from, to); expect(path).toEqual(RelativeFilePath.of("docs/markdown.md")); }); }); diff --git a/packages/commons/fs-utils/src/getAllFilesInDirectory.ts b/packages/commons/fs-utils/src/getAllFilesInDirectory.ts new file mode 100644 index 00000000000..aef96ef7ae0 --- /dev/null +++ b/packages/commons/fs-utils/src/getAllFilesInDirectory.ts @@ -0,0 +1,22 @@ +import { readdir } from "fs/promises"; +import path from "path"; + +export async function getAllFilesInDirectory(dir: string): Promise { + const files = await readdir(dir, { withFileTypes: true }); + const filePromises = files.map(async (file) => { + const filePath = path.join(dir, file.name); + + if (file.isDirectory()) { + return getAllFilesInDirectory(filePath); + } + + if (file.isSymbolicLink() || file.name.startsWith(".")) { + return []; + } + + return filePath; + }); + + const nestedFiles = await Promise.all(filePromises); + return nestedFiles.flat(); +} diff --git a/packages/commons/fs-utils/src/index.ts b/packages/commons/fs-utils/src/index.ts index 36ce4912bbd..2f89ce61c7e 100644 --- a/packages/commons/fs-utils/src/index.ts +++ b/packages/commons/fs-utils/src/index.ts @@ -26,3 +26,4 @@ export { convertToFernHostAbsoluteFilePath, convertToFernHostRelativeFilePath } from "./osPathConverter"; +export { getAllFilesInDirectory } from "./getAllFilesInDirectory"; diff --git a/packages/commons/fs-utils/src/relativize.ts b/packages/commons/fs-utils/src/relativize.ts index 3f1daada676..711ad4017a1 100644 --- a/packages/commons/fs-utils/src/relativize.ts +++ b/packages/commons/fs-utils/src/relativize.ts @@ -2,6 +2,6 @@ import path from "path"; import { AbsoluteFilePath } from "./AbsoluteFilePath"; import { RelativeFilePath } from "./RelativeFilePath"; -export async function relativize(fromPath: AbsoluteFilePath, toPath: AbsoluteFilePath): Promise { +export function relativize(fromPath: AbsoluteFilePath, toPath: AbsoluteFilePath): RelativeFilePath { return RelativeFilePath.of(path.relative(fromPath, toPath)); } diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 696309fe480..56b58af4763 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -34,7 +34,6 @@ }, "devDependencies": { "@fern-api/fern-definition-schema": "workspace:*", - "@fern-api/fs-utils": "workspace:*", "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.8", "@types/lodash-es": "^4.17.12", diff --git a/packages/scripts/src/cli.ts b/packages/scripts/src/cli.ts index 198f03f4d65..2b590c52b61 100644 --- a/packages/scripts/src/cli.ts +++ b/packages/scripts/src/cli.ts @@ -1,10 +1,8 @@ -import { cwd, resolve } from "@fern-api/fs-utils"; import { noop } from "lodash-es"; import { hideBin } from "yargs/helpers"; import yargs from "yargs/yargs"; import { checkReleaseBlockers } from "./checkReleaseBlockers"; import { checkRootPackage } from "./checkRootPackage"; -import { AbsoluteFilePath } from "@fern-api/fs-utils"; import { DefinitionFileSchema } from "@fern-api/fern-definition-schema"; import { writeFile } from "fs/promises"; import prettier from "prettier"; @@ -23,7 +21,8 @@ void yargs(hideBin(process.argv)) }), async (argv) => { const filepath = argv.filepath; - const jsonSchema = zodToJsonSchema(DefinitionFileSchema, "Fern Definition"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const jsonSchema = zodToJsonSchema(DefinitionFileSchema as any, "Fern Definition"); const jsonSchemaStr = JSON.stringify(jsonSchema); const config = (await prettier.resolveConfig(filepath)) ?? undefined; const jsonSchemaFormatted = prettier.format(jsonSchemaStr, { ...config, filepath }); diff --git a/packages/seed/src/commands/test/testWorkspaceFixtures.ts b/packages/seed/src/commands/test/testWorkspaceFixtures.ts index 25584620105..dc544d1eeb6 100644 --- a/packages/seed/src/commands/test/testWorkspaceFixtures.ts +++ b/packages/seed/src/commands/test/testWorkspaceFixtures.ts @@ -7,12 +7,9 @@ import { GeneratorWorkspace } from "../../loadGeneratorWorkspaces"; import { printTestCases } from "./printTestCases"; import { TestRunner } from "./test-runner"; -export const FIXTURES_TO_IGNORE = ["server-sent-event-examples", "server-sent-events"]; export const LANGUAGE_SPECIFIC_FIXTURE_PREFIXES = ["csharp", "go", "java", "python", "ruby", "ts"]; -export const FIXTURES = readDirectories(path.join(__dirname, FERN_DIRECTORY, APIS_DIRECTORY)).filter( - (fixture) => !FIXTURES_TO_IGNORE.includes(fixture) -); +export const FIXTURES = readDirectories(path.join(__dirname, FERN_DIRECTORY, APIS_DIRECTORY)); export async function testGenerator({ runner, @@ -77,7 +74,7 @@ export async function testGenerator({ CONSOLE_LOGGER.info(`Unexpected fixtures include ${unexpectedFixtures.join(", ")}.`); return false; } else { - CONSOLE_LOGGER.info(`All failures were expected.`); + CONSOLE_LOGGER.info("All failures were expected."); } } return true; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 682c67fda5e..5b02cd8e0f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -159,8 +159,8 @@ importers: specifier: workspace:* version: link:../../packages/commons/logging-execa '@fern-fern/generator-cli-sdk': - specifier: 0.0.56 - version: 0.0.56 + specifier: 0.0.17 + version: 0.0.17 '@fern-fern/generator-exec-sdk': specifier: ^0.0.898 version: 0.0.898 @@ -333,8 +333,8 @@ importers: specifier: workspace:* version: link:../../../packages/cli/logger '@fern-fern/generator-cli-sdk': - specifier: 0.0.56 - version: 0.0.56 + specifier: 0.0.17 + version: 0.0.17 '@fern-fern/generator-exec-sdk': specifier: ^0.0.898 version: 0.0.898 @@ -2053,8 +2053,8 @@ importers: specifier: workspace:* version: link:../../codegen '@fern-fern/generator-cli-sdk': - specifier: 0.0.56 - version: 0.0.56 + specifier: 0.0.17 + version: 0.0.17 '@fern-fern/generator-exec-sdk': specifier: ^0.0.898 version: 0.0.898 @@ -3412,6 +3412,104 @@ importers: specifier: ^2.0.5 version: 2.0.5(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5) + packages/cli/docs-importers/commons: + dependencies: + '@fern-api/configuration': + specifier: workspace:* + version: link:../../configuration + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../commons/fs-utils + '@fern-api/task-context': + specifier: workspace:* + version: link:../../task-context + '@fern-fern/fdr-cjs-sdk': + specifier: 0.111.0-51d403bce + version: 0.111.0-51d403bce + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 + devDependencies: + '@types/js-yaml': + specifier: ^4.0.8 + version: 4.0.8 + '@types/node': + specifier: ^18.7.18 + version: 18.7.18 + depcheck: + specifier: ^1.4.6 + version: 1.4.6 + eslint: + specifier: ^8.56.0 + version: 8.56.0 + globals: + specifier: link:@types/vitest/globals + version: link:@types/vitest/globals + organize-imports-cli: + specifier: ^0.10.0 + version: 0.10.0 + prettier: + specifier: ^2.7.1 + version: 2.7.1 + typescript: + specifier: 4.6.4 + version: 4.6.4 + vitest: + specifier: ^2.0.5 + version: 2.0.5(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5) + + packages/cli/docs-importers/mintlify: + dependencies: + '@fern-api/configuration': + specifier: workspace:* + version: link:../../configuration + '@fern-api/core-utils': + specifier: workspace:* + version: link:../../../commons/core-utils + '@fern-api/docs-importer-commons': + specifier: workspace:* + version: link:../commons + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../commons/fs-utils + '@fern-api/logger': + specifier: workspace:* + version: link:../../logger + '@fern-api/task-context': + specifier: workspace:* + version: link:../../task-context + '@fern-fern/fdr-cjs-sdk': + specifier: 0.111.0-51d403bce + version: 0.111.0-51d403bce + gray-matter: + specifier: ^4.0.3 + version: 4.0.3 + devDependencies: + '@types/node': + specifier: ^18.7.18 + version: 18.7.18 + depcheck: + specifier: ^1.4.6 + version: 1.4.6 + eslint: + specifier: ^8.56.0 + version: 8.56.0 + globals: + specifier: link:@types/vitest/globals + version: link:@types/vitest/globals + organize-imports-cli: + specifier: ^0.10.0 + version: 0.10.0 + prettier: + specifier: ^2.7.1 + version: 2.7.1 + typescript: + specifier: 4.6.4 + version: 4.6.4 + vitest: + specifier: ^2.0.5 + version: 2.0.5(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5) + packages/cli/docs-markdown-utils: dependencies: '@fern-api/fs-utils': @@ -5461,6 +5559,9 @@ importers: strip-ansi: specifier: ^7.1.0 version: 7.1.0 + title: + specifier: ^3.5.3 + version: 3.5.3 ua-parser-js: specifier: ^1.0.37 version: 1.0.37 @@ -5477,6 +5578,9 @@ importers: '@types/node': specifier: ^18.7.18 version: 18.7.18 + '@types/title': + specifier: ^3.4.3 + version: 3.4.3 '@types/ua-parser-js': specifier: ^0.7.39 version: 0.7.39 @@ -5697,9 +5801,6 @@ importers: '@fern-api/fern-definition-schema': specifier: workspace:* version: link:../cli/fern-definition/schema - '@fern-api/fs-utils': - specifier: workspace:* - version: link:../commons/fs-utils '@types/jest': specifier: ^29.5.12 version: 29.5.12 @@ -5765,7 +5866,7 @@ importers: version: 17.7.2 zod-to-json-schema: specifier: 3.14.1 - version: 3.14.1(zod@3.22.4) + version: 3.14.1(zod@3.23.8) packages/seed: dependencies: @@ -6925,14 +7026,17 @@ packages: '@fern-fern/fdr-cjs-sdk@0.108.0-dcb0f740c': resolution: {integrity: sha512-AHT1xCWU1iDnJq93pccLxPJVRZycxgig3FCTYWQhipAZOcKeXAUbjza48iwfQD00roxPQoZBuNS1Li3Ez0n4Rw==} + '@fern-fern/fdr-cjs-sdk@0.111.0-51d403bce': + resolution: {integrity: sha512-ia/kqEDaUPiyYtLjCpYNPSO4/tDJDESpdUwEJqbYJP2awdK699L0ptGzNjvzhH0gJxe7/xj7SzPjmh3zs/hI5g==} + '@fern-fern/fdr-test-sdk@0.0.5297': resolution: {integrity: sha512-jrZUZ6oIA64LHtrv77xEq0X7qJhT9xRMGWhKcLjIUArMsD7h6KMWUHVdoB/1MP0Mz/uL/E2xmM31SOgJicpIcA==} '@fern-fern/fiddle-sdk@0.0.584': resolution: {integrity: sha512-92z/Pwo7EDV7PCLwNdxtZiTQpaUpbeXCqKWSLUQwTwFAQEAp+8QKYAA6VJXVQtHLrZg0NqPTXEnE5XF8NbluYA==} - '@fern-fern/generator-cli-sdk@0.0.56': - resolution: {integrity: sha512-pmBwEgTtZ7g9vve419Ia980XmZb9zHl1rmjeC2dwAs0QF9IqNbZ+neVG82lIsR3aeWkIwHwCK5kSiV6lyjQR7A==} + '@fern-fern/generator-cli-sdk@0.0.17': + resolution: {integrity: sha512-uu+Oi9b2KrFo3tj5vqmEShi9CH3FKqNboDtm+3wYO8Z1LAuqo9wZIsxew8EI/bpxc9Xi6gpHi7CLKweKPxjz/Q==} '@fern-fern/generator-exec-sdk@0.0.816': resolution: {integrity: sha512-0i88vvoJjv+sRmSSUrsThJP/Qz4A4uAVUUo8gBXQBKCbjk/76wRxpOAwXh3Twgo/A8XKAfxRA2OBGQ8xv5syXg==} @@ -7659,6 +7763,9 @@ packages: '@types/tinycolor2@1.4.6': resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} + '@types/title@3.4.3': + resolution: {integrity: sha512-mjupLOb4kwUuoUFokkacy/VMRVBH2qtqZ5AX7K7iha6+iKIkX80n/Y4EoNVEVRmer8dYJU/ry+fppUaDFVQh7Q==} + '@types/tmp@0.2.4': resolution: {integrity: sha512-Vq3rwM+2KgiLacq68EjTJD9cuJ/ne5pXntWn8B8Rxj25SLkGAhCgooCZ1lhcIcV5OFveJ+s5Cqpi+XKfFM/xZA==} @@ -12958,6 +13065,9 @@ packages: zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -14038,6 +14148,17 @@ snapshots: transitivePeerDependencies: - encoding + '@fern-fern/fdr-cjs-sdk@0.111.0-51d403bce': + dependencies: + form-data: 4.0.0 + formdata-node: 6.0.3 + js-base64: 3.7.2 + node-fetch: 2.7.0 + qs: 6.11.2 + url-join: 4.0.1 + transitivePeerDependencies: + - encoding + '@fern-fern/fdr-test-sdk@0.0.5297': dependencies: form-data: 4.0.0 @@ -14058,7 +14179,7 @@ snapshots: transitivePeerDependencies: - encoding - '@fern-fern/generator-cli-sdk@0.0.56': {} + '@fern-fern/generator-cli-sdk@0.0.17': {} '@fern-fern/generator-exec-sdk@0.0.816': dependencies: @@ -14923,6 +15044,8 @@ snapshots: '@types/tinycolor2@1.4.6': {} + '@types/title@3.4.3': {} + '@types/tmp@0.2.4': {} '@types/tough-cookie@4.0.5': {} @@ -21298,10 +21421,12 @@ snapshots: yocto-queue@1.0.0: {} - zod-to-json-schema@3.14.1(zod@3.22.4): + zod-to-json-schema@3.14.1(zod@3.23.8): dependencies: - zod: 3.22.4 + zod: 3.23.8 zod@3.22.4: {} + zod@3.23.8: {} + zwitch@2.0.4: {} diff --git a/seed/go-sdk/server-sent-event-examples/.github/workflows/ci.yml b/seed/go-sdk/server-sent-event-examples/.github/workflows/ci.yml new file mode 100644 index 00000000000..d4c0a5dcd95 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Test + run: go test ./... diff --git a/seed/go-sdk/server-sent-event-examples/.mock/definition/api.yml b/seed/go-sdk/server-sent-event-examples/.mock/definition/api.yml new file mode 100644 index 00000000000..80e84c41785 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/.mock/definition/api.yml @@ -0,0 +1 @@ +name: server-sent-events diff --git a/seed/go-sdk/server-sent-event-examples/.mock/definition/completions.yml b/seed/go-sdk/server-sent-event-examples/.mock/definition/completions.yml new file mode 100644 index 00000000000..09a88253331 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/.mock/definition/completions.yml @@ -0,0 +1,36 @@ +types: + StreamedCompletion: + properties: + delta: string + tokens: optional + +service: + auth: false + base-path: "" + endpoints: + stream: + method: POST + path: /stream + request: + name: StreamCompletionRequest + body: + properties: + query: string + response-stream: + type: StreamedCompletion + format: sse + terminator: "[[DONE]]" + examples: + - name: "Stream completions" + request: + query: "foo" + response: + stream: + - event: discriminant-1 + data: + delta: "foo" + tokens: 1 + - event: discriminant-2 + data: + delta: "bar" + tokens: 2 diff --git a/seed/go-sdk/server-sent-event-examples/.mock/fern.config.json b/seed/go-sdk/server-sent-event-examples/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/go-sdk/server-sent-event-examples/.mock/generators.yml b/seed/go-sdk/server-sent-event-examples/.mock/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/.mock/generators.yml @@ -0,0 +1 @@ +{} diff --git a/seed/go-sdk/server-sent-event-examples/client/client.go b/seed/go-sdk/server-sent-event-examples/client/client.go new file mode 100644 index 00000000000..cc1208e999b --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/client/client.go @@ -0,0 +1,33 @@ +// This file was auto-generated by Fern from our API Definition. + +package client + +import ( + completions "github.com/server-sent-event-examples/fern/completions" + core "github.com/server-sent-event-examples/fern/core" + option "github.com/server-sent-event-examples/fern/option" + http "net/http" +) + +type Client struct { + baseURL string + caller *core.Caller + header http.Header + + Completions *completions.Client +} + +func NewClient(opts ...option.RequestOption) *Client { + options := core.NewRequestOptions(opts...) + return &Client{ + baseURL: options.BaseURL, + caller: core.NewCaller( + &core.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + header: options.ToHeader(), + Completions: completions.NewClient(opts...), + } +} diff --git a/seed/go-sdk/server-sent-event-examples/client/client_test.go b/seed/go-sdk/server-sent-event-examples/client/client_test.go new file mode 100644 index 00000000000..a489aeca1f6 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/client/client_test.go @@ -0,0 +1,45 @@ +// This file was auto-generated by Fern from our API Definition. + +package client + +import ( + option "github.com/server-sent-event-examples/fern/option" + assert "github.com/stretchr/testify/assert" + http "net/http" + testing "testing" + time "time" +) + +func TestNewClient(t *testing.T) { + t.Run("default", func(t *testing.T) { + c := NewClient() + assert.Empty(t, c.baseURL) + }) + + t.Run("base url", func(t *testing.T) { + c := NewClient( + option.WithBaseURL("test.co"), + ) + assert.Equal(t, "test.co", c.baseURL) + }) + + t.Run("http client", func(t *testing.T) { + httpClient := &http.Client{ + Timeout: 5 * time.Second, + } + c := NewClient( + option.WithHTTPClient(httpClient), + ) + assert.Empty(t, c.baseURL) + }) + + t.Run("http header", func(t *testing.T) { + header := make(http.Header) + header.Set("X-API-Tenancy", "test") + c := NewClient( + option.WithHTTPHeader(header), + ) + assert.Empty(t, c.baseURL) + assert.Equal(t, "test", c.header.Get("X-API-Tenancy")) + }) +} diff --git a/seed/go-sdk/server-sent-event-examples/completions.go b/seed/go-sdk/server-sent-event-examples/completions.go new file mode 100644 index 00000000000..d51c256586b --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/completions.go @@ -0,0 +1,55 @@ +// This file was auto-generated by Fern from our API Definition. + +package serversentevents + +import ( + json "encoding/json" + fmt "fmt" + core "github.com/server-sent-event-examples/fern/core" +) + +type StreamCompletionRequest struct { + Query string `json:"query" url:"-"` +} + +type StreamedCompletion struct { + Delta string `json:"delta" url:"delta"` + Tokens *int `json:"tokens,omitempty" url:"tokens,omitempty"` + + extraProperties map[string]interface{} + _rawJSON json.RawMessage +} + +func (s *StreamedCompletion) GetExtraProperties() map[string]interface{} { + return s.extraProperties +} + +func (s *StreamedCompletion) UnmarshalJSON(data []byte) error { + type unmarshaler StreamedCompletion + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *s = StreamedCompletion(value) + + extraProperties, err := core.ExtractExtraProperties(data, *s) + if err != nil { + return err + } + s.extraProperties = extraProperties + + s._rawJSON = json.RawMessage(data) + return nil +} + +func (s *StreamedCompletion) String() string { + if len(s._rawJSON) > 0 { + if value, err := core.StringifyJSON(s._rawJSON); err == nil { + return value + } + } + if value, err := core.StringifyJSON(s); err == nil { + return value + } + return fmt.Sprintf("%#v", s) +} diff --git a/seed/go-sdk/server-sent-event-examples/completions/client.go b/seed/go-sdk/server-sent-event-examples/completions/client.go new file mode 100644 index 00000000000..8b4a70bee1b --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/completions/client.go @@ -0,0 +1,68 @@ +// This file was auto-generated by Fern from our API Definition. + +package completions + +import ( + context "context" + fern "github.com/server-sent-event-examples/fern" + core "github.com/server-sent-event-examples/fern/core" + option "github.com/server-sent-event-examples/fern/option" + http "net/http" +) + +type Client struct { + baseURL string + caller *core.Caller + header http.Header +} + +func NewClient(opts ...option.RequestOption) *Client { + options := core.NewRequestOptions(opts...) + return &Client{ + baseURL: options.BaseURL, + caller: core.NewCaller( + &core.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + header: options.ToHeader(), + } +} + +func (c *Client) Stream( + ctx context.Context, + request *fern.StreamCompletionRequest, + opts ...option.RequestOption, +) (*core.Stream[fern.StreamedCompletion], error) { + options := core.NewRequestOptions(opts...) + + baseURL := "" + if c.baseURL != "" { + baseURL = c.baseURL + } + if options.BaseURL != "" { + baseURL = options.BaseURL + } + endpointURL := baseURL + "/stream" + + headers := core.MergeHeaders(c.header.Clone(), options.ToHeader()) + headers.Set("Accept", "text/event-stream") + + streamer := core.NewStreamer[fern.StreamedCompletion](c.caller) + return streamer.Stream( + ctx, + &core.StreamParams{ + URL: endpointURL, + Method: http.MethodPost, + Prefix: core.DefaultSSEDataPrefix, + Terminator: "[[DONE]]", + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Headers: headers, + Client: options.HTTPClient, + Request: request, + }, + ) +} diff --git a/seed/go-sdk/server-sent-event-examples/core/core.go b/seed/go-sdk/server-sent-event-examples/core/core.go new file mode 100644 index 00000000000..6b5a8f3c011 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/core.go @@ -0,0 +1,321 @@ +package core + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "reflect" + "strings" +) + +const ( + // contentType specifies the JSON Content-Type header value. + contentType = "application/json" + contentTypeHeader = "Content-Type" +) + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// EncodeURL encodes the given arguments into the URL, escaping +// values as needed. +func EncodeURL(urlFormat string, args ...interface{}) string { + escapedArgs := make([]interface{}, 0, len(args)) + for _, arg := range args { + escapedArgs = append(escapedArgs, url.PathEscape(fmt.Sprintf("%v", arg))) + } + return fmt.Sprintf(urlFormat, escapedArgs...) +} + +// MergeHeaders merges the given headers together, where the right +// takes precedence over the left. +func MergeHeaders(left, right http.Header) http.Header { + for key, values := range right { + if len(values) > 1 { + left[key] = values + continue + } + if value := right.Get(key); value != "" { + left.Set(key, value) + } + } + return left +} + +// WriteMultipartJSON writes the given value as a JSON part. +// This is used to serialize non-primitive multipart properties +// (i.e. lists, objects, etc). +func WriteMultipartJSON(writer *multipart.Writer, field string, value interface{}) error { + bytes, err := json.Marshal(value) + if err != nil { + return err + } + return writer.WriteField(field, string(bytes)) +} + +// APIError is a lightweight wrapper around the standard error +// interface that preserves the status code from the RPC, if any. +type APIError struct { + err error + + StatusCode int `json:"-"` +} + +// NewAPIError constructs a new API error. +func NewAPIError(statusCode int, err error) *APIError { + return &APIError{ + err: err, + StatusCode: statusCode, + } +} + +// Unwrap returns the underlying error. This also makes the error compatible +// with errors.As and errors.Is. +func (a *APIError) Unwrap() error { + if a == nil { + return nil + } + return a.err +} + +// Error returns the API error's message. +func (a *APIError) Error() string { + if a == nil || (a.err == nil && a.StatusCode == 0) { + return "" + } + if a.err == nil { + return fmt.Sprintf("%d", a.StatusCode) + } + if a.StatusCode == 0 { + return a.err.Error() + } + return fmt.Sprintf("%d: %s", a.StatusCode, a.err.Error()) +} + +// ErrorDecoder decodes *http.Response errors and returns a +// typed API error (e.g. *APIError). +type ErrorDecoder func(statusCode int, body io.Reader) error + +// Caller calls APIs and deserializes their response, if any. +type Caller struct { + client HTTPClient + retrier *Retrier +} + +// CallerParams represents the parameters used to constrcut a new *Caller. +type CallerParams struct { + Client HTTPClient + MaxAttempts uint +} + +// NewCaller returns a new *Caller backed by the given parameters. +func NewCaller(params *CallerParams) *Caller { + var httpClient HTTPClient = http.DefaultClient + if params.Client != nil { + httpClient = params.Client + } + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + return &Caller{ + client: httpClient, + retrier: NewRetrier(retryOptions...), + } +} + +// CallParams represents the parameters used to issue an API call. +type CallParams struct { + URL string + Method string + MaxAttempts uint + Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + Client HTTPClient + Request interface{} + Response interface{} + ResponseIsOptional bool + ErrorDecoder ErrorDecoder +} + +// Call issues an API call according to the given call parameters. +func (c *Caller) Call(ctx context.Context, params *CallParams) error { + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) + if err != nil { + return err + } + + // If the call has been cancelled, don't issue the request. + if err := ctx.Err(); err != nil { + return err + } + + client := c.client + if params.Client != nil { + // Use the HTTP client scoped to the request. + client = params.Client + } + + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + + resp, err := c.retrier.Run( + client.Do, + req, + params.ErrorDecoder, + retryOptions..., + ) + if err != nil { + return err + } + + // Close the response body after we're done. + defer resp.Body.Close() + + // Check if the call was cancelled before we return the error + // associated with the call and/or unmarshal the response data. + if err := ctx.Err(); err != nil { + return err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return decodeError(resp, params.ErrorDecoder) + } + + // Mutate the response parameter in-place. + if params.Response != nil { + if writer, ok := params.Response.(io.Writer); ok { + _, err = io.Copy(writer, resp.Body) + } else { + err = json.NewDecoder(resp.Body).Decode(params.Response) + } + if err != nil { + if err == io.EOF { + if params.ResponseIsOptional { + // The response is optional, so we should ignore the + // io.EOF error + return nil + } + return fmt.Errorf("expected a %T response, but the server responded with nothing", params.Response) + } + return err + } + } + + return nil +} + +// buildURL constructs the final URL by appending the given query parameters (if any). +func buildURL( + url string, + queryParameters url.Values, +) string { + if len(queryParameters) == 0 { + return url + } + if strings.ContainsRune(url, '?') { + url += "&" + } else { + url += "?" + } + url += queryParameters.Encode() + return url +} + +// newRequest returns a new *http.Request with all of the fields +// required to issue the call. +func newRequest( + ctx context.Context, + url string, + method string, + endpointHeaders http.Header, + request interface{}, + bodyProperties map[string]interface{}, +) (*http.Request, error) { + requestBody, err := newRequestBody(request, bodyProperties) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, method, url, requestBody) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set(contentTypeHeader, contentType) + for name, values := range endpointHeaders { + req.Header[name] = values + } + return req, nil +} + +// newRequestBody returns a new io.Reader that represents the HTTP request body. +func newRequestBody(request interface{}, bodyProperties map[string]interface{}) (io.Reader, error) { + if isNil(request) { + if len(bodyProperties) == 0 { + return nil, nil + } + requestBytes, err := json.Marshal(bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil + } + if body, ok := request.(io.Reader); ok { + return body, nil + } + requestBytes, err := MarshalJSONWithExtraProperties(request, bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil +} + +// decodeError decodes the error from the given HTTP response. Note that +// it's the caller's responsibility to close the response body. +func decodeError(response *http.Response, errorDecoder ErrorDecoder) error { + if errorDecoder != nil { + // This endpoint has custom errors, so we'll + // attempt to unmarshal the error into a structured + // type based on the status code. + return errorDecoder(response.StatusCode, response.Body) + } + // This endpoint doesn't have any custom error + // types, so we just read the body as-is, and + // put it into a normal error. + bytes, err := io.ReadAll(response.Body) + if err != nil && err != io.EOF { + return err + } + if err == io.EOF { + // The error didn't have a response body, + // so all we can do is return an error + // with the status code. + return NewAPIError(response.StatusCode, nil) + } + return NewAPIError(response.StatusCode, errors.New(string(bytes))) +} + +// isNil is used to determine if the request value is equal to nil (i.e. an interface +// value that holds a nil concrete value is itself non-nil). +func isNil(value interface{}) bool { + return value == nil || reflect.ValueOf(value).IsNil() +} diff --git a/seed/go-sdk/server-sent-event-examples/core/core_test.go b/seed/go-sdk/server-sent-event-examples/core/core_test.go new file mode 100644 index 00000000000..e6eaef3a86a --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/core_test.go @@ -0,0 +1,390 @@ +package core + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestCase represents a single test case. +type TestCase struct { + description string + + // Server-side assertions. + givePathSuffix string + giveMethod string + giveResponseIsOptional bool + giveHeader http.Header + giveErrorDecoder ErrorDecoder + giveRequest *Request + giveQueryParams url.Values + giveBodyProperties map[string]interface{} + + // Client-side assertions. + wantResponse *Response + wantError error +} + +// Request a simple request body. +type Request struct { + Id string `json:"id"` +} + +// Response a simple response body. +type Response struct { + Id string `json:"id"` + ExtraBodyProperties map[string]interface{} `json:"extraBodyProperties,omitempty"` + QueryParameters url.Values `json:"queryParameters,omitempty"` +} + +// NotFoundError represents a 404. +type NotFoundError struct { + *APIError + + Message string `json:"message"` +} + +func TestCall(t *testing.T) { + tests := []*TestCase{ + { + description: "GET success", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &Request{ + Id: "123", + }, + wantResponse: &Response{ + Id: "123", + }, + }, + { + description: "GET success with query", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &Request{ + Id: "123", + }, + wantResponse: &Response{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + }, + }, + }, + { + description: "GET not found", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &Request{ + Id: strconv.Itoa(http.StatusNotFound), + }, + giveErrorDecoder: newTestErrorDecoder(t), + wantError: &NotFoundError{ + APIError: NewAPIError( + http.StatusNotFound, + errors.New(`{"message":"ID \"404\" not found"}`), + ), + }, + }, + { + description: "POST empty body", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: nil, + wantError: NewAPIError( + http.StatusBadRequest, + errors.New("invalid request"), + ), + }, + { + description: "POST optional response", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &Request{ + Id: "123", + }, + giveResponseIsOptional: true, + }, + { + description: "POST API error", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &Request{ + Id: strconv.Itoa(http.StatusInternalServerError), + }, + wantError: NewAPIError( + http.StatusInternalServerError, + errors.New("failed to process request"), + ), + }, + { + description: "POST extra properties", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: new(Request), + giveBodyProperties: map[string]interface{}{ + "key": "value", + }, + wantResponse: &Response{ + ExtraBodyProperties: map[string]interface{}{ + "key": "value", + }, + }, + }, + { + description: "GET extra query parameters", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + giveRequest: &Request{ + Id: "123", + }, + wantResponse: &Response{ + Id: "123", + QueryParameters: url.Values{ + "extra": []string{"true"}, + }, + }, + }, + { + description: "GET merge extra query parameters", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &Request{ + Id: "123", + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + wantResponse: &Response{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + "extra": []string{"true"}, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + var ( + server = newTestServer(t, test) + client = server.Client() + ) + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + var response *Response + err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL + test.givePathSuffix, + Method: test.giveMethod, + Headers: test.giveHeader, + BodyProperties: test.giveBodyProperties, + QueryParameters: test.giveQueryParams, + Request: test.giveRequest, + Response: &response, + ResponseIsOptional: test.giveResponseIsOptional, + ErrorDecoder: test.giveErrorDecoder, + }, + ) + if test.wantError != nil { + assert.EqualError(t, err, test.wantError.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +func TestMergeHeaders(t *testing.T) { + t.Run("both empty", func(t *testing.T) { + merged := MergeHeaders(make(http.Header), make(http.Header)) + assert.Empty(t, merged) + }) + + t.Run("empty left", func(t *testing.T) { + left := make(http.Header) + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("empty right", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.1") + + right := make(http.Header) + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("single value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.0") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) + + t.Run("multiple value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Versions", "0.0.0") + + right := make(http.Header) + right.Add("X-API-Versions", "0.0.1") + right.Add("X-API-Versions", "0.0.2") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1", "0.0.2"}, merged.Values("X-API-Versions")) + }) + + t.Run("disjoint merge", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Tenancy", "test") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"test"}, merged.Values("X-API-Tenancy")) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) +} + +// newTestServer returns a new *httptest.Server configured with the +// given test parameters. +func newTestServer(t *testing.T, tc *TestCase) *httptest.Server { + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, tc.giveMethod, r.Method) + assert.Equal(t, contentType, r.Header.Get(contentTypeHeader)) + for header, value := range tc.giveHeader { + assert.Equal(t, value, r.Header.Values(header)) + } + + request := new(Request) + + bytes, err := io.ReadAll(r.Body) + if tc.giveRequest == nil { + require.Empty(t, bytes) + w.WriteHeader(http.StatusBadRequest) + _, err = w.Write([]byte("invalid request")) + require.NoError(t, err) + return + } + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + + switch request.Id { + case strconv.Itoa(http.StatusNotFound): + notFoundError := &NotFoundError{ + APIError: &APIError{ + StatusCode: http.StatusNotFound, + }, + Message: fmt.Sprintf("ID %q not found", request.Id), + } + bytes, err = json.Marshal(notFoundError) + require.NoError(t, err) + + w.WriteHeader(http.StatusNotFound) + _, err = w.Write(bytes) + require.NoError(t, err) + return + + case strconv.Itoa(http.StatusInternalServerError): + w.WriteHeader(http.StatusInternalServerError) + _, err = w.Write([]byte("failed to process request")) + require.NoError(t, err) + return + } + + if tc.giveResponseIsOptional { + w.WriteHeader(http.StatusOK) + return + } + + extraBodyProperties := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &extraBodyProperties)) + delete(extraBodyProperties, "id") + + response := &Response{ + Id: request.Id, + ExtraBodyProperties: extraBodyProperties, + QueryParameters: r.URL.Query(), + } + bytes, err = json.Marshal(response) + require.NoError(t, err) + + _, err = w.Write(bytes) + require.NoError(t, err) + }, + ), + ) +} + +// newTestErrorDecoder returns an error decoder suitable for tests. +func newTestErrorDecoder(t *testing.T) func(int, io.Reader) error { + return func(statusCode int, body io.Reader) error { + raw, err := io.ReadAll(body) + require.NoError(t, err) + + var ( + apiError = NewAPIError(statusCode, errors.New(string(raw))) + decoder = json.NewDecoder(bytes.NewReader(raw)) + ) + if statusCode == http.StatusNotFound { + value := new(NotFoundError) + value.APIError = apiError + require.NoError(t, decoder.Decode(value)) + + return value + } + return apiError + } +} diff --git a/seed/go-sdk/server-sent-event-examples/core/extra_properties.go b/seed/go-sdk/server-sent-event-examples/core/extra_properties.go new file mode 100644 index 00000000000..a6af3e12410 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/extra_properties.go @@ -0,0 +1,141 @@ +package core + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-sdk/server-sent-event-examples/core/extra_properties_test.go b/seed/go-sdk/server-sent-event-examples/core/extra_properties_test.go new file mode 100644 index 00000000000..dc66fccd7f1 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/extra_properties_test.go @@ -0,0 +1,228 @@ +package core + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-sdk/server-sent-event-examples/core/query.go b/seed/go-sdk/server-sent-event-examples/core/query.go new file mode 100644 index 00000000000..2670ff7feda --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/query.go @@ -0,0 +1,231 @@ +package core + +import ( + "encoding/base64" + "fmt" + "net/url" + "reflect" + "strings" + "time" + + "github.com/google/uuid" +) + +var ( + bytesType = reflect.TypeOf([]byte{}) + queryEncoderType = reflect.TypeOf(new(QueryEncoder)).Elem() + timeType = reflect.TypeOf(time.Time{}) + uuidType = reflect.TypeOf(uuid.UUID{}) +) + +// QueryEncoder is an interface implemented by any type that wishes to encode +// itself into URL values in a non-standard way. +type QueryEncoder interface { + EncodeQueryValues(key string, v *url.Values) error +} + +// QueryValues encodes url.Values from request objects. +// +// Note: This type is inspired by Google's query encoding library, but +// supports far less customization and is tailored to fit this SDK's use case. +// +// Ref: https://github.com/google/go-querystring +func QueryValues(v interface{}) (url.Values, error) { + values := make(url.Values) + val := reflect.ValueOf(v) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return values, nil + } + val = val.Elem() + } + + if v == nil { + return values, nil + } + + if val.Kind() != reflect.Struct { + return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) + } + + err := reflectValue(values, val, "") + return values, err +} + +// reflectValue populates the values parameter from the struct fields in val. +// Embedded structs are followed recursively (using the rules defined in the +// Values function documentation) breadth-first. +func reflectValue(values url.Values, val reflect.Value, scope string) error { + typ := val.Type() + for i := 0; i < typ.NumField(); i++ { + sf := typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { + // Skip unexported fields. + continue + } + + sv := val.Field(i) + tag := sf.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + + name, opts := parseTag(tag) + if name == "" { + name = sf.Name + } + + if scope != "" { + name = scope + "[" + name + "]" + } + + if opts.Contains("omitempty") && isEmptyValue(sv) { + continue + } + + if sv.Type().Implements(queryEncoderType) { + // If sv is a nil pointer and the custom encoder is defined on a non-pointer + // method receiver, set sv to the zero value of the underlying type + if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(queryEncoderType) { + sv = reflect.New(sv.Type().Elem()) + } + + m := sv.Interface().(QueryEncoder) + if err := m.EncodeQueryValues(name, &values); err != nil { + return err + } + continue + } + + // Recursively dereference pointers, but stop at nil pointers. + for sv.Kind() == reflect.Ptr { + if sv.IsNil() { + break + } + sv = sv.Elem() + } + + if sv.Type() == uuidType || sv.Type() == bytesType || sv.Type() == timeType { + values.Add(name, valueString(sv, opts, sf)) + continue + } + + if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { + if sv.Len() == 0 { + // Skip if slice or array is empty. + continue + } + for i := 0; i < sv.Len(); i++ { + value := sv.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), name); err != nil { + return err + } + } else { + values.Add(name, valueString(value, opts, sf)) + } + } + continue + } + + if sv.Kind() == reflect.Struct { + if err := reflectValue(values, sv, name); err != nil { + return err + } + continue + } + + values.Add(name, valueString(sv, opts, sf)) + } + + return nil +} + +// valueString returns the string representation of a value. +func valueString(v reflect.Value, opts tagOptions, sf reflect.StructField) string { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "" + } + v = v.Elem() + } + + if v.Type() == timeType { + t := v.Interface().(time.Time) + if format := sf.Tag.Get("format"); format == "date" { + return t.Format("2006-01-02") + } + return t.Format(time.RFC3339) + } + + if v.Type() == uuidType { + u := v.Interface().(uuid.UUID) + return u.String() + } + + if v.Type() == bytesType { + b := v.Interface().([]byte) + return base64.StdEncoding.EncodeToString(b) + } + + return fmt.Sprint(v.Interface()) +} + +// isEmptyValue checks if a value should be considered empty for the purposes +// of omitting fields with the "omitempty" option. +func isEmptyValue(v reflect.Value) bool { + type zeroable interface { + IsZero() bool + } + + if !v.IsZero() { + if z, ok := v.Interface().(zeroable); ok { + return z.IsZero() + } + } + + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Invalid, reflect.Complex64, reflect.Complex128, reflect.Chan, reflect.Func, reflect.Struct, reflect.UnsafePointer: + return false + } + + return false +} + +// isStructPointer returns true if the given reflect.Value is a pointer to a struct. +func isStructPointer(v reflect.Value) bool { + return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct +} + +// tagOptions is the string following a comma in a struct field's "url" tag, or +// the empty string. It does not include the leading comma. +type tagOptions []string + +// parseTag splits a struct field's url tag into its name and comma-separated +// options. +func parseTag(tag string) (string, tagOptions) { + s := strings.Split(tag, ",") + return s[0], s[1:] +} + +// Contains checks whether the tagOptions contains the specified option. +func (o tagOptions) Contains(option string) bool { + for _, s := range o { + if s == option { + return true + } + } + return false +} diff --git a/seed/go-sdk/server-sent-event-examples/core/query_test.go b/seed/go-sdk/server-sent-event-examples/core/query_test.go new file mode 100644 index 00000000000..5498fa92aa5 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/query_test.go @@ -0,0 +1,187 @@ +package core + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestQueryValues(t *testing.T) { + t.Run("empty optional", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Empty(t, values) + }) + + t.Run("empty required", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Equal(t, "required=", values.Encode()) + }) + + t.Run("allow multiple", func(t *testing.T) { + type example struct { + Values []string `json:"values" url:"values"` + } + + values, err := QueryValues( + &example{ + Values: []string{"foo", "bar", "baz"}, + }, + ) + require.NoError(t, err) + assert.Equal(t, "values=foo&values=bar&values=baz", values.Encode()) + }) + + t.Run("nested object", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + nestedValue := "nestedValue" + values, err := QueryValues( + &example{ + Required: "requiredValue", + Nested: &nested{ + Value: &nestedValue, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "nested%5Bvalue%5D=nestedValue&required=requiredValue", values.Encode()) + }) + + t.Run("url unspecified", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("url ignored", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound" url:"-"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("datetime", func(t *testing.T) { + type example struct { + DateTime time.Time `json:"dateTime" url:"dateTime"` + } + + values, err := QueryValues( + &example{ + DateTime: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "dateTime=1994-03-16T12%3A34%3A56Z", values.Encode()) + }) + + t.Run("date", func(t *testing.T) { + type example struct { + Date time.Time `json:"date" url:"date" format:"date"` + } + + values, err := QueryValues( + &example{ + Date: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "date=1994-03-16", values.Encode()) + }) + + t.Run("optional time", func(t *testing.T) { + type example struct { + Date *time.Time `json:"date,omitempty" url:"date,omitempty" format:"date"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("omitempty with non-pointer zero value", func(t *testing.T) { + type enum string + + type example struct { + Enum enum `json:"enum,omitempty" url:"enum,omitempty"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("object array", func(t *testing.T) { + type object struct { + Key string `json:"key" url:"key"` + Value string `json:"value" url:"value"` + } + type example struct { + Objects []*object `json:"objects,omitempty" url:"objects,omitempty"` + } + + values, err := QueryValues( + &example{ + Objects: []*object{ + { + Key: "hello", + Value: "world", + }, + { + Key: "foo", + Value: "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "objects%5Bkey%5D=hello&objects%5Bkey%5D=foo&objects%5Bvalue%5D=world&objects%5Bvalue%5D=bar", values.Encode()) + }) +} diff --git a/seed/go-sdk/server-sent-event-examples/core/request_option.go b/seed/go-sdk/server-sent-event-examples/core/request_option.go new file mode 100644 index 00000000000..69ff2743ba5 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/request_option.go @@ -0,0 +1,108 @@ +// This file was auto-generated by Fern from our API Definition. + +package core + +import ( + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of the client or an individual request. +type RequestOption interface { + applyRequestOptions(*RequestOptions) +} + +// RequestOptions defines all of the possible request options. +// +// This type is primarily used by the generated code and is not meant +// to be used directly; use the option package instead. +type RequestOptions struct { + BaseURL string + HTTPClient HTTPClient + HTTPHeader http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + MaxAttempts uint +} + +// NewRequestOptions returns a new *RequestOptions value. +// +// This function is primarily used by the generated code and is not meant +// to be used directly; use RequestOption instead. +func NewRequestOptions(opts ...RequestOption) *RequestOptions { + options := &RequestOptions{ + HTTPHeader: make(http.Header), + BodyProperties: make(map[string]interface{}), + QueryParameters: make(url.Values), + } + for _, opt := range opts { + opt.applyRequestOptions(options) + } + return options +} + +// ToHeader maps the configured request options into a http.Header used +// for the request(s). +func (r *RequestOptions) ToHeader() http.Header { return r.cloneHeader() } + +func (r *RequestOptions) cloneHeader() http.Header { + headers := r.HTTPHeader.Clone() + headers.Set("X-Fern-Language", "Go") + headers.Set("X-Fern-SDK-Name", "github.com/server-sent-event-examples/fern") + headers.Set("X-Fern-SDK-Version", "0.0.1") + return headers +} + +// BaseURLOption implements the RequestOption interface. +type BaseURLOption struct { + BaseURL string +} + +func (b *BaseURLOption) applyRequestOptions(opts *RequestOptions) { + opts.BaseURL = b.BaseURL +} + +// HTTPClientOption implements the RequestOption interface. +type HTTPClientOption struct { + HTTPClient HTTPClient +} + +func (h *HTTPClientOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPClient = h.HTTPClient +} + +// HTTPHeaderOption implements the RequestOption interface. +type HTTPHeaderOption struct { + HTTPHeader http.Header +} + +func (h *HTTPHeaderOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPHeader = h.HTTPHeader +} + +// BodyPropertiesOption implements the RequestOption interface. +type BodyPropertiesOption struct { + BodyProperties map[string]interface{} +} + +func (b *BodyPropertiesOption) applyRequestOptions(opts *RequestOptions) { + opts.BodyProperties = b.BodyProperties +} + +// QueryParametersOption implements the RequestOption interface. +type QueryParametersOption struct { + QueryParameters url.Values +} + +func (q *QueryParametersOption) applyRequestOptions(opts *RequestOptions) { + opts.QueryParameters = q.QueryParameters +} + +// MaxAttemptsOption implements the RequestOption interface. +type MaxAttemptsOption struct { + MaxAttempts uint +} + +func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) { + opts.MaxAttempts = m.MaxAttempts +} diff --git a/seed/go-sdk/server-sent-event-examples/core/retrier.go b/seed/go-sdk/server-sent-event-examples/core/retrier.go new file mode 100644 index 00000000000..ea24916b786 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/retrier.go @@ -0,0 +1,166 @@ +package core + +import ( + "crypto/rand" + "math/big" + "net/http" + "time" +) + +const ( + defaultRetryAttempts = 2 + minRetryDelay = 500 * time.Millisecond + maxRetryDelay = 5000 * time.Millisecond +) + +// RetryOption adapts the behavior the *Retrier. +type RetryOption func(*retryOptions) + +// RetryFunc is a retriable HTTP function call (i.e. *http.Client.Do). +type RetryFunc func(*http.Request) (*http.Response, error) + +// WithMaxAttempts configures the maximum number of attempts +// of the *Retrier. +func WithMaxAttempts(attempts uint) RetryOption { + return func(opts *retryOptions) { + opts.attempts = attempts + } +} + +// Retrier retries failed requests a configurable number of times with an +// exponential back-off between each retry. +type Retrier struct { + attempts uint +} + +// NewRetrier constructs a new *Retrier with the given options, if any. +func NewRetrier(opts ...RetryOption) *Retrier { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + attempts := uint(defaultRetryAttempts) + if options.attempts > 0 { + attempts = options.attempts + } + return &Retrier{ + attempts: attempts, + } +} + +// Run issues the request and, upon failure, retries the request if possible. +// +// The request will be retried as long as the request is deemed retriable and the +// number of retry attempts has not grown larger than the configured retry limit. +func (r *Retrier) Run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + opts ...RetryOption, +) (*http.Response, error) { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + maxRetryAttempts := r.attempts + if options.attempts > 0 { + maxRetryAttempts = options.attempts + } + var ( + retryAttempt uint + previousError error + ) + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt, + previousError, + ) +} + +func (r *Retrier) run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + maxRetryAttempts uint, + retryAttempt uint, + previousError error, +) (*http.Response, error) { + if retryAttempt >= maxRetryAttempts { + return nil, previousError + } + + // If the call has been cancelled, don't issue the request. + if err := request.Context().Err(); err != nil { + return nil, err + } + + response, err := fn(request) + if err != nil { + return nil, err + } + + if r.shouldRetry(response) { + defer response.Body.Close() + + delay, err := r.retryDelay(retryAttempt) + if err != nil { + return nil, err + } + + time.Sleep(delay) + + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt+1, + decodeError(response, errorDecoder), + ) + } + + return response, nil +} + +// shouldRetry returns true if the request should be retried based on the given +// response status code. +func (r *Retrier) shouldRetry(response *http.Response) bool { + return response.StatusCode == http.StatusTooManyRequests || + response.StatusCode == http.StatusRequestTimeout || + response.StatusCode == http.StatusConflict || + response.StatusCode >= http.StatusInternalServerError +} + +// retryDelay calculates the delay time in milliseconds based on the retry attempt. +func (r *Retrier) retryDelay(retryAttempt uint) (time.Duration, error) { + // Apply exponential backoff. + delay := minRetryDelay + minRetryDelay*time.Duration(retryAttempt*retryAttempt) + + // Do not allow the number to exceed maxRetryDelay. + if delay > maxRetryDelay { + delay = maxRetryDelay + } + + // Apply some itter by randomizing the value in the range of 75%-100%. + max := big.NewInt(int64(delay / 4)) + jitter, err := rand.Int(rand.Reader, max) + if err != nil { + return 0, err + } + + delay -= time.Duration(jitter.Int64()) + + // Never sleep less than the base sleep seconds. + if delay < minRetryDelay { + delay = minRetryDelay + } + + return delay, nil +} + +type retryOptions struct { + attempts uint +} diff --git a/seed/go-sdk/server-sent-event-examples/core/stream.go b/seed/go-sdk/server-sent-event-examples/core/stream.go new file mode 100644 index 00000000000..30e374dc4e2 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/stream.go @@ -0,0 +1,294 @@ +package core + +import ( + "bufio" + "context" + "encoding/json" + "io" + "net/http" + "net/url" + "strings" +) + +const ( + // DefaultDataPrefix is the default prefix used for SSE streaming. + DefaultSSEDataPrefix = "data: " + + // DefaultTerminator is the default terminator used for SSE streaming. + DefaultSSETerminator = "[DONE]" + + // The default stream delimiter used to split messages. + defaultStreamDelimiter = '\n' +) + +// Streamer calls APIs and streams responses using a *Stream. +type Streamer[T any] struct { + client HTTPClient + retrier *Retrier +} + +// NewStreamer returns a new *Streamer backed by the given caller's HTTP client. +func NewStreamer[T any](caller *Caller) *Streamer[T] { + return &Streamer[T]{ + client: caller.client, + retrier: caller.retrier, + } +} + +// StreamParams represents the parameters used to issue an API streaming call. +type StreamParams struct { + URL string + Method string + Prefix string + Delimiter string + Terminator string + MaxAttempts uint + Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + Client HTTPClient + Request interface{} + ErrorDecoder ErrorDecoder +} + +// Stream issues an API streaming call according to the given stream parameters. +func (s *Streamer[T]) Stream(ctx context.Context, params *StreamParams) (*Stream[T], error) { + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) + if err != nil { + return nil, err + } + + // If the call has been cancelled, don't issue the request. + if err := ctx.Err(); err != nil { + return nil, err + } + + client := s.client + if params.Client != nil { + // Use the HTTP client scoped to the request. + client = params.Client + } + + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + + resp, err := s.retrier.Run( + client.Do, + req, + params.ErrorDecoder, + retryOptions..., + ) + if err != nil { + return nil, err + } + + // Check if the call was cancelled before we return the error + // associated with the call and/or unmarshal the response data. + if err := ctx.Err(); err != nil { + defer resp.Body.Close() + return nil, err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + defer resp.Body.Close() + return nil, decodeError(resp, params.ErrorDecoder) + } + + var opts []StreamOption + if params.Delimiter != "" { + opts = append(opts, WithDelimiter(params.Delimiter)) + } + if params.Prefix != "" { + opts = append(opts, WithPrefix(params.Prefix)) + } + if params.Terminator != "" { + opts = append(opts, WithTerminator(params.Terminator)) + } + + return NewStream[T](resp, opts...), nil +} + +// Stream represents a stream of messages sent from a server. +type Stream[T any] struct { + reader streamReader + closer io.Closer +} + +// StreamOption adapts the behavior of the Stream. +type StreamOption func(*streamOptions) + +// WithDelimiter overrides the delimiter for the Stream. +// +// By default, the Stream is newline-delimited. +func WithDelimiter(delimiter string) StreamOption { + return func(opts *streamOptions) { + opts.delimiter = delimiter + } +} + +// WithPrefix overrides the prefix for the Stream. +// +// By default, the Stream doesn't have a prefix. +func WithPrefix(prefix string) StreamOption { + return func(opts *streamOptions) { + opts.prefix = prefix + } +} + +// WithTerminator overrides the terminator for the Stream. +// +// By default, the Stream terminates on EOF. +func WithTerminator(terminator string) StreamOption { + return func(opts *streamOptions) { + opts.terminator = terminator + } +} + +// NewStream constructs a new Stream from the given *http.Response. +func NewStream[T any](response *http.Response, opts ...StreamOption) *Stream[T] { + options := new(streamOptions) + for _, opt := range opts { + opt(options) + } + return &Stream[T]{ + reader: newStreamReader(response.Body, options), + closer: response.Body, + } +} + +// Recv reads a message from the stream, returning io.EOF when +// all the messages have been read. +func (s Stream[T]) Recv() (T, error) { + var value T + bytes, err := s.reader.ReadFromStream() + if err != nil { + return value, err + } + if err := json.Unmarshal(bytes, &value); err != nil { + return value, err + } + return value, nil +} + +// Close closes the Stream. +func (s Stream[T]) Close() error { + return s.closer.Close() +} + +// streamReader reads data from a stream. +type streamReader interface { + ReadFromStream() ([]byte, error) +} + +// newStreamReader returns a new streamReader based on the given +// delimiter. +// +// By default, the streamReader uses a simple a *bufio.Reader +// which splits on newlines, and otherwise use a *bufio.Scanner to +// split on custom delimiters. +func newStreamReader(reader io.Reader, options *streamOptions) streamReader { + if !options.isEmpty() { + if options.delimiter == "" { + options.delimiter = string(defaultStreamDelimiter) + } + return newScannerStreamReader(reader, options) + } + return newBufferStreamReader(reader) +} + +// bufferStreamReader reads data from a *bufio.Reader, which splits +// on newlines. +type bufferStreamReader struct { + reader *bufio.Reader +} + +func newBufferStreamReader(reader io.Reader) *bufferStreamReader { + return &bufferStreamReader{ + reader: bufio.NewReader(reader), + } +} + +func (b *bufferStreamReader) ReadFromStream() ([]byte, error) { + return b.reader.ReadBytes(defaultStreamDelimiter) +} + +// scannerStreamReader reads data from a *bufio.Scanner, which allows for +// configurable delimiters. +type scannerStreamReader struct { + scanner *bufio.Scanner + options *streamOptions +} + +func newScannerStreamReader( + reader io.Reader, + options *streamOptions, +) *scannerStreamReader { + scanner := bufio.NewScanner(reader) + stream := &scannerStreamReader{ + scanner: scanner, + options: options, + } + scanner.Split(func(bytes []byte, atEOF bool) (int, []byte, error) { + if atEOF && len(bytes) == 0 { + return 0, nil, nil + } + n, data, err := stream.parse(bytes) + if stream.isTerminated(data) { + return 0, nil, io.EOF + } + return n, data, err + }) + return stream +} + +func (s *scannerStreamReader) ReadFromStream() ([]byte, error) { + if s.scanner.Scan() { + return s.scanner.Bytes(), nil + } + if err := s.scanner.Err(); err != nil { + return nil, err + } + return nil, io.EOF +} + +func (s *scannerStreamReader) parse(bytes []byte) (int, []byte, error) { + var start int + if s.options != nil && s.options.prefix != "" { + if i := strings.Index(string(bytes), s.options.prefix); i >= 0 { + start = i + len(s.options.prefix) + } + } + data := bytes[start:] + if i := strings.Index(string(data), s.options.delimiter); i >= 0 { + data = data[:i+len(s.options.delimiter)] + } + n := start + len(data) + len(s.options.delimiter) + return n, data, nil +} + +func (s *scannerStreamReader) isTerminated(bytes []byte) bool { + if s.options == nil || s.options.terminator == "" { + return false + } + return strings.Contains(string(bytes), s.options.terminator) +} + +type streamOptions struct { + delimiter string + prefix string + terminator string +} + +func (s *streamOptions) isEmpty() bool { + return s.delimiter == "" && s.prefix == "" && s.terminator == "" +} diff --git a/seed/go-sdk/server-sent-event-examples/core/stringer.go b/seed/go-sdk/server-sent-event-examples/core/stringer.go new file mode 100644 index 00000000000..000cf448641 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/stringer.go @@ -0,0 +1,13 @@ +package core + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-sdk/server-sent-event-examples/core/time.go b/seed/go-sdk/server-sent-event-examples/core/time.go new file mode 100644 index 00000000000..d009ab30c90 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/core/time.go @@ -0,0 +1,137 @@ +package core + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-sdk/server-sent-event-examples/go.mod b/seed/go-sdk/server-sent-event-examples/go.mod new file mode 100644 index 00000000000..06b3f73e21d --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/go.mod @@ -0,0 +1,14 @@ +module github.com/server-sent-event-examples/fern + +go 1.18 + +require ( + github.com/google/uuid v1.4.0 + github.com/stretchr/testify v1.7.0 +) + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/seed/go-sdk/server-sent-event-examples/go.sum b/seed/go-sdk/server-sent-event-examples/go.sum new file mode 100644 index 00000000000..b3766d4366b --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/go.sum @@ -0,0 +1,14 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-sdk/server-sent-event-examples/option/request_option.go b/seed/go-sdk/server-sent-event-examples/option/request_option.go new file mode 100644 index 00000000000..4216f1eb7d3 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/option/request_option.go @@ -0,0 +1,64 @@ +// This file was auto-generated by Fern from our API Definition. + +package option + +import ( + core "github.com/server-sent-event-examples/fern/core" + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of an indivdual request. +type RequestOption = core.RequestOption + +// WithBaseURL sets the base URL, overriding the default +// environment, if any. +func WithBaseURL(baseURL string) *core.BaseURLOption { + return &core.BaseURLOption{ + BaseURL: baseURL, + } +} + +// WithHTTPClient uses the given HTTPClient to issue the request. +func WithHTTPClient(httpClient core.HTTPClient) *core.HTTPClientOption { + return &core.HTTPClientOption{ + HTTPClient: httpClient, + } +} + +// WithHTTPHeader adds the given http.Header to the request. +func WithHTTPHeader(httpHeader http.Header) *core.HTTPHeaderOption { + return &core.HTTPHeaderOption{ + // Clone the headers so they can't be modified after the option call. + HTTPHeader: httpHeader.Clone(), + } +} + +// WithBodyProperties adds the given body properties to the request. +func WithBodyProperties(bodyProperties map[string]interface{}) *core.BodyPropertiesOption { + copiedBodyProperties := make(map[string]interface{}, len(bodyProperties)) + for key, value := range bodyProperties { + copiedBodyProperties[key] = value + } + return &core.BodyPropertiesOption{ + BodyProperties: copiedBodyProperties, + } +} + +// WithQueryParameters adds the given query parameters to the request. +func WithQueryParameters(queryParameters url.Values) *core.QueryParametersOption { + copiedQueryParameters := make(url.Values, len(queryParameters)) + for key, values := range queryParameters { + copiedQueryParameters[key] = values + } + return &core.QueryParametersOption{ + QueryParameters: copiedQueryParameters, + } +} + +// WithMaxAttempts configures the maximum number of retry attempts. +func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { + return &core.MaxAttemptsOption{ + MaxAttempts: attempts, + } +} diff --git a/seed/go-sdk/server-sent-event-examples/pointer.go b/seed/go-sdk/server-sent-event-examples/pointer.go new file mode 100644 index 00000000000..70309429210 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/pointer.go @@ -0,0 +1,132 @@ +package serversentevents + +import ( + "time" + + "github.com/google/uuid" +) + +// Bool returns a pointer to the given bool value. +func Bool(b bool) *bool { + return &b +} + +// Byte returns a pointer to the given byte value. +func Byte(b byte) *byte { + return &b +} + +// Complex64 returns a pointer to the given complex64 value. +func Complex64(c complex64) *complex64 { + return &c +} + +// Complex128 returns a pointer to the given complex128 value. +func Complex128(c complex128) *complex128 { + return &c +} + +// Float32 returns a pointer to the given float32 value. +func Float32(f float32) *float32 { + return &f +} + +// Float64 returns a pointer to the given float64 value. +func Float64(f float64) *float64 { + return &f +} + +// Int returns a pointer to the given int value. +func Int(i int) *int { + return &i +} + +// Int8 returns a pointer to the given int8 value. +func Int8(i int8) *int8 { + return &i +} + +// Int16 returns a pointer to the given int16 value. +func Int16(i int16) *int16 { + return &i +} + +// Int32 returns a pointer to the given int32 value. +func Int32(i int32) *int32 { + return &i +} + +// Int64 returns a pointer to the given int64 value. +func Int64(i int64) *int64 { + return &i +} + +// Rune returns a pointer to the given rune value. +func Rune(r rune) *rune { + return &r +} + +// String returns a pointer to the given string value. +func String(s string) *string { + return &s +} + +// Uint returns a pointer to the given uint value. +func Uint(u uint) *uint { + return &u +} + +// Uint8 returns a pointer to the given uint8 value. +func Uint8(u uint8) *uint8 { + return &u +} + +// Uint16 returns a pointer to the given uint16 value. +func Uint16(u uint16) *uint16 { + return &u +} + +// Uint32 returns a pointer to the given uint32 value. +func Uint32(u uint32) *uint32 { + return &u +} + +// Uint64 returns a pointer to the given uint64 value. +func Uint64(u uint64) *uint64 { + return &u +} + +// Uintptr returns a pointer to the given uintptr value. +func Uintptr(u uintptr) *uintptr { + return &u +} + +// UUID returns a pointer to the given uuid.UUID value. +func UUID(u uuid.UUID) *uuid.UUID { + return &u +} + +// Time returns a pointer to the given time.Time value. +func Time(t time.Time) *time.Time { + return &t +} + +// MustParseDate attempts to parse the given string as a +// date time.Time, and panics upon failure. +func MustParseDate(date string) time.Time { + t, err := time.Parse("2006-01-02", date) + if err != nil { + panic(err) + } + return t +} + +// MustParseDateTime attempts to parse the given string as a +// datetime time.Time, and panics upon failure. +func MustParseDateTime(datetime string) time.Time { + t, err := time.Parse(time.RFC3339, datetime) + if err != nil { + panic(err) + } + return t +} diff --git a/seed/go-sdk/server-sent-event-examples/snippet-templates.json b/seed/go-sdk/server-sent-event-examples/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/go-sdk/server-sent-event-examples/snippet.json b/seed/go-sdk/server-sent-event-examples/snippet.json new file mode 100644 index 00000000000..406119ebfc0 --- /dev/null +++ b/seed/go-sdk/server-sent-event-examples/snippet.json @@ -0,0 +1,15 @@ +{ + "endpoints": [ + { + "id": { + "path": "/stream", + "method": "POST", + "identifier_override": "endpoint_completions.stream" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/server-sent-event-examples/fern\"\n\tfernclient \"github.com/server-sent-event-examples/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Completions.Stream(\n\tcontext.TODO(),\n\t\u0026fern.StreamCompletionRequest{\n\t\tQuery: \"foo\",\n\t},\n)\n" + } + } + ] +} \ No newline at end of file diff --git a/seed/go-sdk/server-sent-events/.mock/definition/completions.yml b/seed/go-sdk/server-sent-events/.mock/definition/completions.yml index feb12b103ab..d1748fad19e 100644 --- a/seed/go-sdk/server-sent-events/.mock/definition/completions.yml +++ b/seed/go-sdk/server-sent-events/.mock/definition/completions.yml @@ -1,6 +1,6 @@ types: - StreamedCompletion: - properties: + StreamedCompletion: + properties: delta: string tokens: optional @@ -11,10 +11,10 @@ service: stream: method: POST path: /stream - request: + request: name: StreamCompletionRequest - body: - properties: + body: + properties: query: string response-stream: type: StreamedCompletion diff --git a/seed/go-sdk/server-sent-events/completions.go b/seed/go-sdk/server-sent-events/completions.go index 6aab2c31783..6ecfba31ebf 100644 --- a/seed/go-sdk/server-sent-events/completions.go +++ b/seed/go-sdk/server-sent-events/completions.go @@ -9,14 +9,19 @@ import ( ) type StreamCompletionRequest struct { - Query string `json:"query" url:"query"` + Query string `json:"query" url:"-"` } type StreamedCompletion struct { Delta string `json:"delta" url:"delta"` Tokens *int `json:"tokens,omitempty" url:"tokens,omitempty"` - _rawJSON json.RawMessage + extraProperties map[string]interface{} + _rawJSON json.RawMessage +} + +func (s *StreamedCompletion) GetExtraProperties() map[string]interface{} { + return s.extraProperties } func (s *StreamedCompletion) UnmarshalJSON(data []byte) error { @@ -26,6 +31,13 @@ func (s *StreamedCompletion) UnmarshalJSON(data []byte) error { return err } *s = StreamedCompletion(value) + + extraProperties, err := core.ExtractExtraProperties(data, *s) + if err != nil { + return err + } + s.extraProperties = extraProperties + s._rawJSON = json.RawMessage(data) return nil } diff --git a/seed/go-sdk/server-sent-events/completions/client.go b/seed/go-sdk/server-sent-events/completions/client.go index 4ec024d0338..7852aa17953 100644 --- a/seed/go-sdk/server-sent-events/completions/client.go +++ b/seed/go-sdk/server-sent-events/completions/client.go @@ -34,7 +34,7 @@ func (c *Client) Stream( ctx context.Context, request *fern.StreamCompletionRequest, opts ...option.RequestOption, -) error { +) (*core.Stream[fern.StreamedCompletion], error) { options := core.NewRequestOptions(opts...) baseURL := "" @@ -44,22 +44,25 @@ func (c *Client) Stream( if options.BaseURL != "" { baseURL = options.BaseURL } - endpointURL := baseURL + "/" + "stream" + endpointURL := baseURL + "/stream" headers := core.MergeHeaders(c.header.Clone(), options.ToHeader()) + headers.Set("Accept", "text/event-stream") - if err := c.caller.Call( + streamer := core.NewStreamer[fern.StreamedCompletion](c.caller) + return streamer.Stream( ctx, - &core.CallParams{ - URL: endpointURL, - Method: http.MethodPost, - MaxAttempts: options.MaxAttempts, - Headers: headers, - Client: options.HTTPClient, - Request: request, + &core.StreamParams{ + URL: endpointURL, + Method: http.MethodPost, + Prefix: core.DefaultSSEDataPrefix, + Terminator: "[[DONE]]", + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Headers: headers, + Client: options.HTTPClient, + Request: request, }, - ); err != nil { - return err - } - return nil + ) } diff --git a/seed/go-sdk/server-sent-events/core/core.go b/seed/go-sdk/server-sent-events/core/core.go index 5277d138d27..6b5a8f3c011 100644 --- a/seed/go-sdk/server-sent-events/core/core.go +++ b/seed/go-sdk/server-sent-events/core/core.go @@ -9,6 +9,9 @@ import ( "io" "mime/multipart" "net/http" + "net/url" + "reflect" + "strings" ) const ( @@ -22,6 +25,16 @@ type HTTPClient interface { Do(*http.Request) (*http.Response, error) } +// EncodeURL encodes the given arguments into the URL, escaping +// values as needed. +func EncodeURL(urlFormat string, args ...interface{}) string { + escapedArgs := make([]interface{}, 0, len(args)) + for _, arg := range args { + escapedArgs = append(escapedArgs, url.PathEscape(fmt.Sprintf("%v", arg))) + } + return fmt.Sprintf(urlFormat, escapedArgs...) +} + // MergeHeaders merges the given headers together, where the right // takes precedence over the left. func MergeHeaders(left, right http.Header) http.Header { @@ -125,6 +138,8 @@ type CallParams struct { Method string MaxAttempts uint Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values Client HTTPClient Request interface{} Response interface{} @@ -134,7 +149,15 @@ type CallParams struct { // Call issues an API call according to the given call parameters. func (c *Caller) Call(ctx context.Context, params *CallParams) error { - req, err := newRequest(ctx, params.URL, params.Method, params.Headers, params.Request) + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) if err != nil { return err } @@ -201,6 +224,23 @@ func (c *Caller) Call(ctx context.Context, params *CallParams) error { return nil } +// buildURL constructs the final URL by appending the given query parameters (if any). +func buildURL( + url string, + queryParameters url.Values, +) string { + if len(queryParameters) == 0 { + return url + } + if strings.ContainsRune(url, '?') { + url += "&" + } else { + url += "?" + } + url += queryParameters.Encode() + return url +} + // newRequest returns a new *http.Request with all of the fields // required to issue the call. func newRequest( @@ -209,8 +249,9 @@ func newRequest( method string, endpointHeaders http.Header, request interface{}, + bodyProperties map[string]interface{}, ) (*http.Request, error) { - requestBody, err := newRequestBody(request) + requestBody, err := newRequestBody(request, bodyProperties) if err != nil { return nil, err } @@ -227,20 +268,25 @@ func newRequest( } // newRequestBody returns a new io.Reader that represents the HTTP request body. -func newRequestBody(request interface{}) (io.Reader, error) { - var requestBody io.Reader - if request != nil { - if body, ok := request.(io.Reader); ok { - requestBody = body - } else { - requestBytes, err := json.Marshal(request) - if err != nil { - return nil, err - } - requestBody = bytes.NewReader(requestBytes) +func newRequestBody(request interface{}, bodyProperties map[string]interface{}) (io.Reader, error) { + if isNil(request) { + if len(bodyProperties) == 0 { + return nil, nil + } + requestBytes, err := json.Marshal(bodyProperties) + if err != nil { + return nil, err } + return bytes.NewReader(requestBytes), nil } - return requestBody, nil + if body, ok := request.(io.Reader); ok { + return body, nil + } + requestBytes, err := MarshalJSONWithExtraProperties(request, bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil } // decodeError decodes the error from the given HTTP response. Note that @@ -267,3 +313,9 @@ func decodeError(response *http.Response, errorDecoder ErrorDecoder) error { } return NewAPIError(response.StatusCode, errors.New(string(bytes))) } + +// isNil is used to determine if the request value is equal to nil (i.e. an interface +// value that holds a nil concrete value is itself non-nil). +func isNil(value interface{}) bool { + return value == nil || reflect.ValueOf(value).IsNil() +} diff --git a/seed/go-sdk/server-sent-events/core/core_test.go b/seed/go-sdk/server-sent-events/core/core_test.go index f476f9ee383..e6eaef3a86a 100644 --- a/seed/go-sdk/server-sent-events/core/core_test.go +++ b/seed/go-sdk/server-sent-events/core/core_test.go @@ -9,6 +9,7 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "strconv" "testing" @@ -21,11 +22,14 @@ type TestCase struct { description string // Server-side assertions. + givePathSuffix string giveMethod string giveResponseIsOptional bool giveHeader http.Header giveErrorDecoder ErrorDecoder giveRequest *Request + giveQueryParams url.Values + giveBodyProperties map[string]interface{} // Client-side assertions. wantResponse *Response @@ -39,7 +43,9 @@ type Request struct { // Response a simple response body. type Response struct { - Id string `json:"id"` + Id string `json:"id"` + ExtraBodyProperties map[string]interface{} `json:"extraBodyProperties,omitempty"` + QueryParameters url.Values `json:"queryParameters,omitempty"` } // NotFoundError represents a 404. @@ -64,6 +70,23 @@ func TestCall(t *testing.T) { Id: "123", }, }, + { + description: "GET success with query", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &Request{ + Id: "123", + }, + wantResponse: &Response{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + }, + }, + }, { description: "GET not found", giveMethod: http.MethodGet, @@ -81,6 +104,18 @@ func TestCall(t *testing.T) { ), }, }, + { + description: "POST empty body", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: nil, + wantError: NewAPIError( + http.StatusBadRequest, + errors.New("invalid request"), + ), + }, { description: "POST optional response", giveMethod: http.MethodPost, @@ -106,6 +141,62 @@ func TestCall(t *testing.T) { errors.New("failed to process request"), ), }, + { + description: "POST extra properties", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: new(Request), + giveBodyProperties: map[string]interface{}{ + "key": "value", + }, + wantResponse: &Response{ + ExtraBodyProperties: map[string]interface{}{ + "key": "value", + }, + }, + }, + { + description: "GET extra query parameters", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + giveRequest: &Request{ + Id: "123", + }, + wantResponse: &Response{ + Id: "123", + QueryParameters: url.Values{ + "extra": []string{"true"}, + }, + }, + }, + { + description: "GET merge extra query parameters", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &Request{ + Id: "123", + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + wantResponse: &Response{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + "extra": []string{"true"}, + }, + }, + }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { @@ -122,9 +213,11 @@ func TestCall(t *testing.T) { err := caller.Call( context.Background(), &CallParams{ - URL: server.URL, + URL: server.URL + test.givePathSuffix, Method: test.giveMethod, Headers: test.giveHeader, + BodyProperties: test.giveBodyProperties, + QueryParameters: test.giveQueryParams, Request: test.giveRequest, Response: &response, ResponseIsOptional: test.giveResponseIsOptional, @@ -215,10 +308,17 @@ func newTestServer(t *testing.T, tc *TestCase) *httptest.Server { assert.Equal(t, value, r.Header.Values(header)) } + request := new(Request) + bytes, err := io.ReadAll(r.Body) + if tc.giveRequest == nil { + require.Empty(t, bytes) + w.WriteHeader(http.StatusBadRequest) + _, err = w.Write([]byte("invalid request")) + require.NoError(t, err) + return + } require.NoError(t, err) - - request := new(Request) require.NoError(t, json.Unmarshal(bytes, request)) switch request.Id { @@ -249,8 +349,14 @@ func newTestServer(t *testing.T, tc *TestCase) *httptest.Server { return } + extraBodyProperties := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &extraBodyProperties)) + delete(extraBodyProperties, "id") + response := &Response{ - Id: request.Id, + Id: request.Id, + ExtraBodyProperties: extraBodyProperties, + QueryParameters: r.URL.Query(), } bytes, err = json.Marshal(response) require.NoError(t, err) diff --git a/seed/go-sdk/server-sent-events/core/extra_properties.go b/seed/go-sdk/server-sent-events/core/extra_properties.go new file mode 100644 index 00000000000..a6af3e12410 --- /dev/null +++ b/seed/go-sdk/server-sent-events/core/extra_properties.go @@ -0,0 +1,141 @@ +package core + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-sdk/server-sent-events/core/extra_properties_test.go b/seed/go-sdk/server-sent-events/core/extra_properties_test.go new file mode 100644 index 00000000000..dc66fccd7f1 --- /dev/null +++ b/seed/go-sdk/server-sent-events/core/extra_properties_test.go @@ -0,0 +1,228 @@ +package core + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-sdk/server-sent-events/core/query.go b/seed/go-sdk/server-sent-events/core/query.go index 479cbb24d18..2670ff7feda 100644 --- a/seed/go-sdk/server-sent-events/core/query.go +++ b/seed/go-sdk/server-sent-events/core/query.go @@ -116,7 +116,14 @@ func reflectValue(values url.Values, val reflect.Value, scope string) error { continue } for i := 0; i < sv.Len(); i++ { - values.Add(name, valueString(sv.Index(i), opts, sf)) + value := sv.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), name); err != nil { + return err + } + } else { + values.Add(name, valueString(value, opts, sf)) + } } continue } @@ -171,7 +178,7 @@ func isEmptyValue(v reflect.Value) bool { IsZero() bool } - if !v.IsNil() { + if !v.IsZero() { if z, ok := v.Interface().(zeroable); ok { return z.IsZero() } @@ -197,6 +204,11 @@ func isEmptyValue(v reflect.Value) bool { return false } +// isStructPointer returns true if the given reflect.Value is a pointer to a struct. +func isStructPointer(v reflect.Value) bool { + return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct +} + // tagOptions is the string following a comma in a struct field's "url" tag, or // the empty string. It does not include the leading comma. type tagOptions []string diff --git a/seed/go-sdk/server-sent-events/core/query_test.go b/seed/go-sdk/server-sent-events/core/query_test.go index 4f0d39284f4..5498fa92aa5 100644 --- a/seed/go-sdk/server-sent-events/core/query_test.go +++ b/seed/go-sdk/server-sent-events/core/query_test.go @@ -143,4 +143,45 @@ func TestQueryValues(t *testing.T) { require.NoError(t, err) assert.Empty(t, values.Encode()) }) + + t.Run("omitempty with non-pointer zero value", func(t *testing.T) { + type enum string + + type example struct { + Enum enum `json:"enum,omitempty" url:"enum,omitempty"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("object array", func(t *testing.T) { + type object struct { + Key string `json:"key" url:"key"` + Value string `json:"value" url:"value"` + } + type example struct { + Objects []*object `json:"objects,omitempty" url:"objects,omitempty"` + } + + values, err := QueryValues( + &example{ + Objects: []*object{ + { + Key: "hello", + Value: "world", + }, + { + Key: "foo", + Value: "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "objects%5Bkey%5D=hello&objects%5Bkey%5D=foo&objects%5Bvalue%5D=world&objects%5Bvalue%5D=bar", values.Encode()) + }) } diff --git a/seed/go-sdk/server-sent-events/core/request_option.go b/seed/go-sdk/server-sent-events/core/request_option.go index 3bc434c91ca..c25ed836172 100644 --- a/seed/go-sdk/server-sent-events/core/request_option.go +++ b/seed/go-sdk/server-sent-events/core/request_option.go @@ -4,6 +4,7 @@ package core import ( http "net/http" + url "net/url" ) // RequestOption adapts the behavior of the client or an individual request. @@ -16,10 +17,12 @@ type RequestOption interface { // This type is primarily used by the generated code and is not meant // to be used directly; use the option package instead. type RequestOptions struct { - BaseURL string - HTTPClient HTTPClient - HTTPHeader http.Header - MaxAttempts uint + BaseURL string + HTTPClient HTTPClient + HTTPHeader http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + MaxAttempts uint } // NewRequestOptions returns a new *RequestOptions value. @@ -28,7 +31,9 @@ type RequestOptions struct { // to be used directly; use RequestOption instead. func NewRequestOptions(opts ...RequestOption) *RequestOptions { options := &RequestOptions{ - HTTPHeader: make(http.Header), + HTTPHeader: make(http.Header), + BodyProperties: make(map[string]interface{}), + QueryParameters: make(url.Values), } for _, opt := range opts { opt.applyRequestOptions(options) @@ -75,6 +80,24 @@ func (h *HTTPHeaderOption) applyRequestOptions(opts *RequestOptions) { opts.HTTPHeader = h.HTTPHeader } +// BodyPropertiesOption implements the RequestOption interface. +type BodyPropertiesOption struct { + BodyProperties map[string]interface{} +} + +func (b *BodyPropertiesOption) applyRequestOptions(opts *RequestOptions) { + opts.BodyProperties = b.BodyProperties +} + +// QueryParametersOption implements the RequestOption interface. +type QueryParametersOption struct { + QueryParameters url.Values +} + +func (q *QueryParametersOption) applyRequestOptions(opts *RequestOptions) { + opts.QueryParameters = q.QueryParameters +} + // MaxAttemptsOption implements the RequestOption interface. type MaxAttemptsOption struct { MaxAttempts uint diff --git a/seed/go-sdk/server-sent-events/core/stream.go b/seed/go-sdk/server-sent-events/core/stream.go index 5d97e936450..30e374dc4e2 100644 --- a/seed/go-sdk/server-sent-events/core/stream.go +++ b/seed/go-sdk/server-sent-events/core/stream.go @@ -6,10 +6,20 @@ import ( "encoding/json" "io" "net/http" + "net/url" "strings" ) -const defaultStreamDelimiter = '\n' +const ( + // DefaultDataPrefix is the default prefix used for SSE streaming. + DefaultSSEDataPrefix = "data: " + + // DefaultTerminator is the default terminator used for SSE streaming. + DefaultSSETerminator = "[DONE]" + + // The default stream delimiter used to split messages. + defaultStreamDelimiter = '\n' +) // Streamer calls APIs and streams responses using a *Stream. type Streamer[T any] struct { @@ -27,19 +37,31 @@ func NewStreamer[T any](caller *Caller) *Streamer[T] { // StreamParams represents the parameters used to issue an API streaming call. type StreamParams struct { - URL string - Method string - Delimiter string - MaxAttempts uint - Headers http.Header - Client HTTPClient - Request interface{} - ErrorDecoder ErrorDecoder + URL string + Method string + Prefix string + Delimiter string + Terminator string + MaxAttempts uint + Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + Client HTTPClient + Request interface{} + ErrorDecoder ErrorDecoder } // Stream issues an API streaming call according to the given stream parameters. func (s *Streamer[T]) Stream(ctx context.Context, params *StreamParams) (*Stream[T], error) { - req, err := newRequest(ctx, params.URL, params.Method, params.Headers, params.Request) + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) if err != nil { return nil, err } @@ -86,6 +108,13 @@ func (s *Streamer[T]) Stream(ctx context.Context, params *StreamParams) (*Stream if params.Delimiter != "" { opts = append(opts, WithDelimiter(params.Delimiter)) } + if params.Prefix != "" { + opts = append(opts, WithPrefix(params.Prefix)) + } + if params.Terminator != "" { + opts = append(opts, WithTerminator(params.Terminator)) + } + return NewStream[T](resp, opts...), nil } @@ -107,6 +136,24 @@ func WithDelimiter(delimiter string) StreamOption { } } +// WithPrefix overrides the prefix for the Stream. +// +// By default, the Stream doesn't have a prefix. +func WithPrefix(prefix string) StreamOption { + return func(opts *streamOptions) { + opts.prefix = prefix + } +} + +// WithTerminator overrides the terminator for the Stream. +// +// By default, the Stream terminates on EOF. +func WithTerminator(terminator string) StreamOption { + return func(opts *streamOptions) { + opts.terminator = terminator + } +} + // NewStream constructs a new Stream from the given *http.Response. func NewStream[T any](response *http.Response, opts ...StreamOption) *Stream[T] { options := new(streamOptions) @@ -114,7 +161,7 @@ func NewStream[T any](response *http.Response, opts ...StreamOption) *Stream[T] opt(options) } return &Stream[T]{ - reader: newStreamReader(response.Body, options.delimiter), + reader: newStreamReader(response.Body, options), closer: response.Body, } } @@ -149,9 +196,12 @@ type streamReader interface { // By default, the streamReader uses a simple a *bufio.Reader // which splits on newlines, and otherwise use a *bufio.Scanner to // split on custom delimiters. -func newStreamReader(reader io.Reader, delimiter string) streamReader { - if len(delimiter) > 0 { - return newScannerStreamReader(reader, delimiter) +func newStreamReader(reader io.Reader, options *streamOptions) streamReader { + if !options.isEmpty() { + if options.delimiter == "" { + options.delimiter = string(defaultStreamDelimiter) + } + return newScannerStreamReader(reader, options) } return newBufferStreamReader(reader) } @@ -176,37 +226,69 @@ func (b *bufferStreamReader) ReadFromStream() ([]byte, error) { // configurable delimiters. type scannerStreamReader struct { scanner *bufio.Scanner + options *streamOptions } -func newScannerStreamReader(reader io.Reader, delimiter string) *scannerStreamReader { +func newScannerStreamReader( + reader io.Reader, + options *streamOptions, +) *scannerStreamReader { scanner := bufio.NewScanner(reader) - scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) { - if atEOF && len(data) == 0 { + stream := &scannerStreamReader{ + scanner: scanner, + options: options, + } + scanner.Split(func(bytes []byte, atEOF bool) (int, []byte, error) { + if atEOF && len(bytes) == 0 { return 0, nil, nil } - if i := strings.Index(string(data), delimiter); i >= 0 { - return i + len(delimiter), data[0:i], nil - } - if atEOF { - return len(data), data, nil + n, data, err := stream.parse(bytes) + if stream.isTerminated(data) { + return 0, nil, io.EOF } - return 0, nil, nil + return n, data, err }) - return &scannerStreamReader{ - scanner: scanner, - } + return stream } -func (b *scannerStreamReader) ReadFromStream() ([]byte, error) { - if b.scanner.Scan() { - return b.scanner.Bytes(), nil +func (s *scannerStreamReader) ReadFromStream() ([]byte, error) { + if s.scanner.Scan() { + return s.scanner.Bytes(), nil } - if err := b.scanner.Err(); err != nil { + if err := s.scanner.Err(); err != nil { return nil, err } return nil, io.EOF } +func (s *scannerStreamReader) parse(bytes []byte) (int, []byte, error) { + var start int + if s.options != nil && s.options.prefix != "" { + if i := strings.Index(string(bytes), s.options.prefix); i >= 0 { + start = i + len(s.options.prefix) + } + } + data := bytes[start:] + if i := strings.Index(string(data), s.options.delimiter); i >= 0 { + data = data[:i+len(s.options.delimiter)] + } + n := start + len(data) + len(s.options.delimiter) + return n, data, nil +} + +func (s *scannerStreamReader) isTerminated(bytes []byte) bool { + if s.options == nil || s.options.terminator == "" { + return false + } + return strings.Contains(string(bytes), s.options.terminator) +} + type streamOptions struct { - delimiter string + delimiter string + prefix string + terminator string +} + +func (s *streamOptions) isEmpty() bool { + return s.delimiter == "" && s.prefix == "" && s.terminator == "" } diff --git a/seed/go-sdk/server-sent-events/option/request_option.go b/seed/go-sdk/server-sent-events/option/request_option.go index 3e78103f5f7..35fafda6e85 100644 --- a/seed/go-sdk/server-sent-events/option/request_option.go +++ b/seed/go-sdk/server-sent-events/option/request_option.go @@ -5,6 +5,7 @@ package option import ( core "github.com/server-sent-events/fern/core" http "net/http" + url "net/url" ) // RequestOption adapts the behavior of an indivdual request. @@ -33,6 +34,28 @@ func WithHTTPHeader(httpHeader http.Header) *core.HTTPHeaderOption { } } +// WithBodyProperties adds the given body properties to the request. +func WithBodyProperties(bodyProperties map[string]interface{}) *core.BodyPropertiesOption { + copiedBodyProperties := make(map[string]interface{}, len(bodyProperties)) + for key, value := range bodyProperties { + copiedBodyProperties[key] = value + } + return &core.BodyPropertiesOption{ + BodyProperties: copiedBodyProperties, + } +} + +// WithQueryParameters adds the given query parameters to the request. +func WithQueryParameters(queryParameters url.Values) *core.QueryParametersOption { + copiedQueryParameters := make(url.Values, len(queryParameters)) + for key, values := range queryParameters { + copiedQueryParameters[key] = values + } + return &core.QueryParametersOption{ + QueryParameters: copiedQueryParameters, + } +} + // WithMaxAttempts configures the maximum number of retry attempts. func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { return &core.MaxAttemptsOption{ diff --git a/seed/go-sdk/server-sent-events/snippet.json b/seed/go-sdk/server-sent-events/snippet.json index e69de29bb2d..41e784e2610 100644 --- a/seed/go-sdk/server-sent-events/snippet.json +++ b/seed/go-sdk/server-sent-events/snippet.json @@ -0,0 +1,15 @@ +{ + "endpoints": [ + { + "id": { + "path": "/stream", + "method": "POST", + "identifier_override": "endpoint_completions.stream" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/server-sent-events/fern\"\n\tfernclient \"github.com/server-sent-events/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Completions.Stream(\n\tcontext.TODO(),\n\t\u0026fern.StreamCompletionRequest{\n\t\tQuery: \"string\",\n\t},\n)\n" + } + } + ] +} \ No newline at end of file diff --git a/seed/go-sdk/streaming/core/stream.go b/seed/go-sdk/streaming/core/stream.go index c610ad3963e..30e374dc4e2 100644 --- a/seed/go-sdk/streaming/core/stream.go +++ b/seed/go-sdk/streaming/core/stream.go @@ -10,7 +10,16 @@ import ( "strings" ) -const defaultStreamDelimiter = '\n' +const ( + // DefaultDataPrefix is the default prefix used for SSE streaming. + DefaultSSEDataPrefix = "data: " + + // DefaultTerminator is the default terminator used for SSE streaming. + DefaultSSETerminator = "[DONE]" + + // The default stream delimiter used to split messages. + defaultStreamDelimiter = '\n' +) // Streamer calls APIs and streams responses using a *Stream. type Streamer[T any] struct { @@ -30,7 +39,9 @@ func NewStreamer[T any](caller *Caller) *Streamer[T] { type StreamParams struct { URL string Method string + Prefix string Delimiter string + Terminator string MaxAttempts uint Headers http.Header BodyProperties map[string]interface{} @@ -97,6 +108,13 @@ func (s *Streamer[T]) Stream(ctx context.Context, params *StreamParams) (*Stream if params.Delimiter != "" { opts = append(opts, WithDelimiter(params.Delimiter)) } + if params.Prefix != "" { + opts = append(opts, WithPrefix(params.Prefix)) + } + if params.Terminator != "" { + opts = append(opts, WithTerminator(params.Terminator)) + } + return NewStream[T](resp, opts...), nil } @@ -118,6 +136,24 @@ func WithDelimiter(delimiter string) StreamOption { } } +// WithPrefix overrides the prefix for the Stream. +// +// By default, the Stream doesn't have a prefix. +func WithPrefix(prefix string) StreamOption { + return func(opts *streamOptions) { + opts.prefix = prefix + } +} + +// WithTerminator overrides the terminator for the Stream. +// +// By default, the Stream terminates on EOF. +func WithTerminator(terminator string) StreamOption { + return func(opts *streamOptions) { + opts.terminator = terminator + } +} + // NewStream constructs a new Stream from the given *http.Response. func NewStream[T any](response *http.Response, opts ...StreamOption) *Stream[T] { options := new(streamOptions) @@ -125,7 +161,7 @@ func NewStream[T any](response *http.Response, opts ...StreamOption) *Stream[T] opt(options) } return &Stream[T]{ - reader: newStreamReader(response.Body, options.delimiter), + reader: newStreamReader(response.Body, options), closer: response.Body, } } @@ -160,9 +196,12 @@ type streamReader interface { // By default, the streamReader uses a simple a *bufio.Reader // which splits on newlines, and otherwise use a *bufio.Scanner to // split on custom delimiters. -func newStreamReader(reader io.Reader, delimiter string) streamReader { - if len(delimiter) > 0 { - return newScannerStreamReader(reader, delimiter) +func newStreamReader(reader io.Reader, options *streamOptions) streamReader { + if !options.isEmpty() { + if options.delimiter == "" { + options.delimiter = string(defaultStreamDelimiter) + } + return newScannerStreamReader(reader, options) } return newBufferStreamReader(reader) } @@ -187,37 +226,69 @@ func (b *bufferStreamReader) ReadFromStream() ([]byte, error) { // configurable delimiters. type scannerStreamReader struct { scanner *bufio.Scanner + options *streamOptions } -func newScannerStreamReader(reader io.Reader, delimiter string) *scannerStreamReader { +func newScannerStreamReader( + reader io.Reader, + options *streamOptions, +) *scannerStreamReader { scanner := bufio.NewScanner(reader) - scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) { - if atEOF && len(data) == 0 { + stream := &scannerStreamReader{ + scanner: scanner, + options: options, + } + scanner.Split(func(bytes []byte, atEOF bool) (int, []byte, error) { + if atEOF && len(bytes) == 0 { return 0, nil, nil } - if i := strings.Index(string(data), delimiter); i >= 0 { - return i + len(delimiter), data[0:i], nil - } - if atEOF { - return len(data), data, nil + n, data, err := stream.parse(bytes) + if stream.isTerminated(data) { + return 0, nil, io.EOF } - return 0, nil, nil + return n, data, err }) - return &scannerStreamReader{ - scanner: scanner, - } + return stream } -func (b *scannerStreamReader) ReadFromStream() ([]byte, error) { - if b.scanner.Scan() { - return b.scanner.Bytes(), nil +func (s *scannerStreamReader) ReadFromStream() ([]byte, error) { + if s.scanner.Scan() { + return s.scanner.Bytes(), nil } - if err := b.scanner.Err(); err != nil { + if err := s.scanner.Err(); err != nil { return nil, err } return nil, io.EOF } +func (s *scannerStreamReader) parse(bytes []byte) (int, []byte, error) { + var start int + if s.options != nil && s.options.prefix != "" { + if i := strings.Index(string(bytes), s.options.prefix); i >= 0 { + start = i + len(s.options.prefix) + } + } + data := bytes[start:] + if i := strings.Index(string(data), s.options.delimiter); i >= 0 { + data = data[:i+len(s.options.delimiter)] + } + n := start + len(data) + len(s.options.delimiter) + return n, data, nil +} + +func (s *scannerStreamReader) isTerminated(bytes []byte) bool { + if s.options == nil || s.options.terminator == "" { + return false + } + return strings.Contains(string(bytes), s.options.terminator) +} + type streamOptions struct { - delimiter string + delimiter string + prefix string + terminator string +} + +func (s *streamOptions) isEmpty() bool { + return s.delimiter == "" && s.prefix == "" && s.terminator == "" } diff --git a/seed/java-sdk/alias/src/main/java/com/seed/alias/core/FileStream.java b/seed/java-sdk/alias/src/main/java/com/seed/alias/core/FileStream.java new file mode 100644 index 00000000000..1ae8b9862eb --- /dev/null +++ b/seed/java-sdk/alias/src/main/java/com/seed/alias/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.alias.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/alias/src/main/java/com/seed/alias/core/InputStreamRequestBody.java b/seed/java-sdk/alias/src/main/java/com/seed/alias/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6e2753b51fc --- /dev/null +++ b/seed/java-sdk/alias/src/main/java/com/seed/alias/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.alias.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/core/FileStream.java b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/core/FileStream.java new file mode 100644 index 00000000000..38b7d83d9e2 --- /dev/null +++ b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.anyAuth.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/core/InputStreamRequestBody.java b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..07213f610fd --- /dev/null +++ b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.anyAuth.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/api-wide-base-path/src/main/java/com/seed/apiWideBasePath/core/FileStream.java b/seed/java-sdk/api-wide-base-path/src/main/java/com/seed/apiWideBasePath/core/FileStream.java new file mode 100644 index 00000000000..fc61f6613b3 --- /dev/null +++ b/seed/java-sdk/api-wide-base-path/src/main/java/com/seed/apiWideBasePath/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.apiWideBasePath.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/api-wide-base-path/src/main/java/com/seed/apiWideBasePath/core/InputStreamRequestBody.java b/seed/java-sdk/api-wide-base-path/src/main/java/com/seed/apiWideBasePath/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..252b78ca845 --- /dev/null +++ b/seed/java-sdk/api-wide-base-path/src/main/java/com/seed/apiWideBasePath/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.apiWideBasePath.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/audiences/src/main/java/com/seed/audiences/core/FileStream.java b/seed/java-sdk/audiences/src/main/java/com/seed/audiences/core/FileStream.java new file mode 100644 index 00000000000..8b5e4faf59f --- /dev/null +++ b/seed/java-sdk/audiences/src/main/java/com/seed/audiences/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.audiences.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/audiences/src/main/java/com/seed/audiences/core/InputStreamRequestBody.java b/seed/java-sdk/audiences/src/main/java/com/seed/audiences/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..862223b76e1 --- /dev/null +++ b/seed/java-sdk/audiences/src/main/java/com/seed/audiences/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.audiences.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/auth-environment-variables/src/main/java/com/seed/authEnvironmentVariables/core/FileStream.java b/seed/java-sdk/auth-environment-variables/src/main/java/com/seed/authEnvironmentVariables/core/FileStream.java new file mode 100644 index 00000000000..34fe1522766 --- /dev/null +++ b/seed/java-sdk/auth-environment-variables/src/main/java/com/seed/authEnvironmentVariables/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.authEnvironmentVariables.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/auth-environment-variables/src/main/java/com/seed/authEnvironmentVariables/core/InputStreamRequestBody.java b/seed/java-sdk/auth-environment-variables/src/main/java/com/seed/authEnvironmentVariables/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..0d791937e28 --- /dev/null +++ b/seed/java-sdk/auth-environment-variables/src/main/java/com/seed/authEnvironmentVariables/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.authEnvironmentVariables.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/basic-auth-environment-variables/src/main/java/com/seed/basicAuthEnvironmentVariables/core/FileStream.java b/seed/java-sdk/basic-auth-environment-variables/src/main/java/com/seed/basicAuthEnvironmentVariables/core/FileStream.java new file mode 100644 index 00000000000..e81b3d982f4 --- /dev/null +++ b/seed/java-sdk/basic-auth-environment-variables/src/main/java/com/seed/basicAuthEnvironmentVariables/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.basicAuthEnvironmentVariables.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/basic-auth-environment-variables/src/main/java/com/seed/basicAuthEnvironmentVariables/core/InputStreamRequestBody.java b/seed/java-sdk/basic-auth-environment-variables/src/main/java/com/seed/basicAuthEnvironmentVariables/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6beb92826c2 --- /dev/null +++ b/seed/java-sdk/basic-auth-environment-variables/src/main/java/com/seed/basicAuthEnvironmentVariables/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.basicAuthEnvironmentVariables.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/basic-auth/src/main/java/com/seed/basicAuth/core/FileStream.java b/seed/java-sdk/basic-auth/src/main/java/com/seed/basicAuth/core/FileStream.java new file mode 100644 index 00000000000..7c4ed563899 --- /dev/null +++ b/seed/java-sdk/basic-auth/src/main/java/com/seed/basicAuth/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.basicAuth.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/basic-auth/src/main/java/com/seed/basicAuth/core/InputStreamRequestBody.java b/seed/java-sdk/basic-auth/src/main/java/com/seed/basicAuth/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..e6ed44ad19f --- /dev/null +++ b/seed/java-sdk/basic-auth/src/main/java/com/seed/basicAuth/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.basicAuth.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/bearer-token-environment-variable/src/main/java/com/seed/bearerTokenEnvironmentVariable/core/FileStream.java b/seed/java-sdk/bearer-token-environment-variable/src/main/java/com/seed/bearerTokenEnvironmentVariable/core/FileStream.java new file mode 100644 index 00000000000..731cd11b89a --- /dev/null +++ b/seed/java-sdk/bearer-token-environment-variable/src/main/java/com/seed/bearerTokenEnvironmentVariable/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.bearerTokenEnvironmentVariable.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/bearer-token-environment-variable/src/main/java/com/seed/bearerTokenEnvironmentVariable/core/InputStreamRequestBody.java b/seed/java-sdk/bearer-token-environment-variable/src/main/java/com/seed/bearerTokenEnvironmentVariable/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..554cc2fa139 --- /dev/null +++ b/seed/java-sdk/bearer-token-environment-variable/src/main/java/com/seed/bearerTokenEnvironmentVariable/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.bearerTokenEnvironmentVariable.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/bytes/src/main/java/com/seed/bytes/core/FileStream.java b/seed/java-sdk/bytes/src/main/java/com/seed/bytes/core/FileStream.java new file mode 100644 index 00000000000..94cbb816c29 --- /dev/null +++ b/seed/java-sdk/bytes/src/main/java/com/seed/bytes/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.bytes.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/bytes/src/main/java/com/seed/bytes/core/InputStreamRequestBody.java b/seed/java-sdk/bytes/src/main/java/com/seed/bytes/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..88563a4b6dd --- /dev/null +++ b/seed/java-sdk/bytes/src/main/java/com/seed/bytes/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.bytes.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/bytes/src/main/java/com/seed/bytes/resources/service/ServiceClient.java b/seed/java-sdk/bytes/src/main/java/com/seed/bytes/resources/service/ServiceClient.java index 2481457ad55..54b342c6d1f 100644 --- a/seed/java-sdk/bytes/src/main/java/com/seed/bytes/resources/service/ServiceClient.java +++ b/seed/java-sdk/bytes/src/main/java/com/seed/bytes/resources/service/ServiceClient.java @@ -4,13 +4,17 @@ package com.seed.bytes.resources.service; import com.seed.bytes.core.ClientOptions; +import com.seed.bytes.core.InputStreamRequestBody; import com.seed.bytes.core.ObjectMappers; import com.seed.bytes.core.RequestOptions; import com.seed.bytes.core.SeedBytesApiException; import com.seed.bytes.core.SeedBytesException; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -24,21 +28,20 @@ public ServiceClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; } - public void upload(byte[] request) { + public void upload(InputStream request) { upload(request, null); } - public void upload(byte[] request, RequestOptions requestOptions) { + public void upload(InputStream request, RequestOptions requestOptions) { HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) .newBuilder() .addPathSegments("upload-content") .build(); - RequestBody body = RequestBody.create(request); + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); Request okhttpRequest = new Request.Builder() .url(httpUrl) .method("POST", body) .headers(Headers.of(clientOptions.headers(requestOptions))) - .addHeader("Content-Type", "application/octet-stream") .build(); OkHttpClient client = clientOptions.httpClient(); if (requestOptions != null && requestOptions.getTimeout().isPresent()) { @@ -58,4 +61,12 @@ public void upload(byte[] request, RequestOptions requestOptions) { throw new SeedBytesException("Network error executing HTTP request", e); } } + + public void upload(byte[] request) { + upload(new ByteArrayInputStream(request)); + } + + public void upload(byte[] request, RequestOptions requestOptions) { + upload(new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/circular-references-advanced/src/main/java/com/seed/api/core/FileStream.java b/seed/java-sdk/circular-references-advanced/src/main/java/com/seed/api/core/FileStream.java new file mode 100644 index 00000000000..a71e7946986 --- /dev/null +++ b/seed/java-sdk/circular-references-advanced/src/main/java/com/seed/api/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/circular-references-advanced/src/main/java/com/seed/api/core/InputStreamRequestBody.java b/seed/java-sdk/circular-references-advanced/src/main/java/com/seed/api/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..fac2b602f6d --- /dev/null +++ b/seed/java-sdk/circular-references-advanced/src/main/java/com/seed/api/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/circular-references/src/main/java/com/seed/api/core/FileStream.java b/seed/java-sdk/circular-references/src/main/java/com/seed/api/core/FileStream.java new file mode 100644 index 00000000000..a71e7946986 --- /dev/null +++ b/seed/java-sdk/circular-references/src/main/java/com/seed/api/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/circular-references/src/main/java/com/seed/api/core/InputStreamRequestBody.java b/seed/java-sdk/circular-references/src/main/java/com/seed/api/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..fac2b602f6d --- /dev/null +++ b/seed/java-sdk/circular-references/src/main/java/com/seed/api/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/cross-package-type-names/src/main/java/com/seed/crossPackageTypeNames/core/FileStream.java b/seed/java-sdk/cross-package-type-names/src/main/java/com/seed/crossPackageTypeNames/core/FileStream.java new file mode 100644 index 00000000000..37bf59bc169 --- /dev/null +++ b/seed/java-sdk/cross-package-type-names/src/main/java/com/seed/crossPackageTypeNames/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.crossPackageTypeNames.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/cross-package-type-names/src/main/java/com/seed/crossPackageTypeNames/core/InputStreamRequestBody.java b/seed/java-sdk/cross-package-type-names/src/main/java/com/seed/crossPackageTypeNames/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..408c6fb4cc0 --- /dev/null +++ b/seed/java-sdk/cross-package-type-names/src/main/java/com/seed/crossPackageTypeNames/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.crossPackageTypeNames.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/custom-auth/src/main/java/com/seed/customAuth/core/FileStream.java b/seed/java-sdk/custom-auth/src/main/java/com/seed/customAuth/core/FileStream.java new file mode 100644 index 00000000000..af7f757fd6e --- /dev/null +++ b/seed/java-sdk/custom-auth/src/main/java/com/seed/customAuth/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.customAuth.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/custom-auth/src/main/java/com/seed/customAuth/core/InputStreamRequestBody.java b/seed/java-sdk/custom-auth/src/main/java/com/seed/customAuth/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..4702fa34430 --- /dev/null +++ b/seed/java-sdk/custom-auth/src/main/java/com/seed/customAuth/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.customAuth.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/error-property/src/main/java/com/seed/errorProperty/core/FileStream.java b/seed/java-sdk/error-property/src/main/java/com/seed/errorProperty/core/FileStream.java new file mode 100644 index 00000000000..d43b98a5af1 --- /dev/null +++ b/seed/java-sdk/error-property/src/main/java/com/seed/errorProperty/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.errorProperty.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/error-property/src/main/java/com/seed/errorProperty/core/InputStreamRequestBody.java b/seed/java-sdk/error-property/src/main/java/com/seed/errorProperty/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..60b6f4b6a55 --- /dev/null +++ b/seed/java-sdk/error-property/src/main/java/com/seed/errorProperty/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.errorProperty.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/examples/src/main/java/com/seed/examples/core/FileStream.java b/seed/java-sdk/examples/src/main/java/com/seed/examples/core/FileStream.java new file mode 100644 index 00000000000..b8baa202210 --- /dev/null +++ b/seed/java-sdk/examples/src/main/java/com/seed/examples/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.examples.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/examples/src/main/java/com/seed/examples/core/InputStreamRequestBody.java b/seed/java-sdk/examples/src/main/java/com/seed/examples/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..9f7528dadd8 --- /dev/null +++ b/seed/java-sdk/examples/src/main/java/com/seed/examples/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.examples.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/core/FileStream.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/core/FileStream.java new file mode 100644 index 00000000000..23c6d0fcfa6 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6b85198c022 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/core/FileStream.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/core/FileStream.java new file mode 100644 index 00000000000..23c6d0fcfa6 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6b85198c022 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/core/FileStream.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/core/FileStream.java new file mode 100644 index 00000000000..23c6d0fcfa6 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6b85198c022 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/core/FileStream.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/core/FileStream.java new file mode 100644 index 00000000000..23c6d0fcfa6 --- /dev/null +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6b85198c022 --- /dev/null +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/core/FileStream.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/core/FileStream.java new file mode 100644 index 00000000000..23c6d0fcfa6 --- /dev/null +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6b85198c022 --- /dev/null +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/core/FileStream.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/core/FileStream.java new file mode 100644 index 00000000000..23c6d0fcfa6 --- /dev/null +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6b85198c022 --- /dev/null +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/exhaustive/local-files/core/FileStream.java b/seed/java-sdk/exhaustive/local-files/core/FileStream.java new file mode 100644 index 00000000000..da09acd7d86 --- /dev/null +++ b/seed/java-sdk/exhaustive/local-files/core/FileStream.java @@ -0,0 +1,61 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.fern.sdk.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/core/InputStreamRequestBody.java b/seed/java-sdk/exhaustive/local-files/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..ee5889df856 --- /dev/null +++ b/seed/java-sdk/exhaustive/local-files/core/InputStreamRequestBody.java @@ -0,0 +1,80 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.fern.sdk.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/core/FileStream.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/core/FileStream.java new file mode 100644 index 00000000000..23c6d0fcfa6 --- /dev/null +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6b85198c022 --- /dev/null +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/extra-properties/src/main/java/com/seed/extraProperties/core/FileStream.java b/seed/java-sdk/extra-properties/src/main/java/com/seed/extraProperties/core/FileStream.java new file mode 100644 index 00000000000..7431b745ec5 --- /dev/null +++ b/seed/java-sdk/extra-properties/src/main/java/com/seed/extraProperties/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.extraProperties.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/extra-properties/src/main/java/com/seed/extraProperties/core/InputStreamRequestBody.java b/seed/java-sdk/extra-properties/src/main/java/com/seed/extraProperties/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..f8efc649d8c --- /dev/null +++ b/seed/java-sdk/extra-properties/src/main/java/com/seed/extraProperties/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.extraProperties.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/file-download/src/main/java/com/seed/fileDownload/core/FileStream.java b/seed/java-sdk/file-download/src/main/java/com/seed/fileDownload/core/FileStream.java new file mode 100644 index 00000000000..a33ad14c4f0 --- /dev/null +++ b/seed/java-sdk/file-download/src/main/java/com/seed/fileDownload/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.fileDownload.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/file-download/src/main/java/com/seed/fileDownload/core/InputStreamRequestBody.java b/seed/java-sdk/file-download/src/main/java/com/seed/fileDownload/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..c6eeb098ac6 --- /dev/null +++ b/seed/java-sdk/file-download/src/main/java/com/seed/fileDownload/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.fileDownload.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/file-upload/src/main/java/com/seed/fileUpload/core/FileStream.java b/seed/java-sdk/file-upload/src/main/java/com/seed/fileUpload/core/FileStream.java new file mode 100644 index 00000000000..7e0d88ba4f4 --- /dev/null +++ b/seed/java-sdk/file-upload/src/main/java/com/seed/fileUpload/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.fileUpload.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/file-upload/src/main/java/com/seed/fileUpload/core/InputStreamRequestBody.java b/seed/java-sdk/file-upload/src/main/java/com/seed/fileUpload/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..3cfb56452e0 --- /dev/null +++ b/seed/java-sdk/file-upload/src/main/java/com/seed/fileUpload/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.fileUpload.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/folders/src/main/java/com/seed/api/core/FileStream.java b/seed/java-sdk/folders/src/main/java/com/seed/api/core/FileStream.java new file mode 100644 index 00000000000..a71e7946986 --- /dev/null +++ b/seed/java-sdk/folders/src/main/java/com/seed/api/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/folders/src/main/java/com/seed/api/core/InputStreamRequestBody.java b/seed/java-sdk/folders/src/main/java/com/seed/api/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..fac2b602f6d --- /dev/null +++ b/seed/java-sdk/folders/src/main/java/com/seed/api/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/grpc-proto-exhaustive/src/main/java/com/seed/api/core/FileStream.java b/seed/java-sdk/grpc-proto-exhaustive/src/main/java/com/seed/api/core/FileStream.java new file mode 100644 index 00000000000..a71e7946986 --- /dev/null +++ b/seed/java-sdk/grpc-proto-exhaustive/src/main/java/com/seed/api/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/grpc-proto-exhaustive/src/main/java/com/seed/api/core/InputStreamRequestBody.java b/seed/java-sdk/grpc-proto-exhaustive/src/main/java/com/seed/api/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..fac2b602f6d --- /dev/null +++ b/seed/java-sdk/grpc-proto-exhaustive/src/main/java/com/seed/api/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/grpc-proto/src/main/java/com/seed/api/core/FileStream.java b/seed/java-sdk/grpc-proto/src/main/java/com/seed/api/core/FileStream.java new file mode 100644 index 00000000000..a71e7946986 --- /dev/null +++ b/seed/java-sdk/grpc-proto/src/main/java/com/seed/api/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/grpc-proto/src/main/java/com/seed/api/core/InputStreamRequestBody.java b/seed/java-sdk/grpc-proto/src/main/java/com/seed/api/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..fac2b602f6d --- /dev/null +++ b/seed/java-sdk/grpc-proto/src/main/java/com/seed/api/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/idempotency-headers/src/main/java/com/seed/idempotencyHeaders/core/FileStream.java b/seed/java-sdk/idempotency-headers/src/main/java/com/seed/idempotencyHeaders/core/FileStream.java new file mode 100644 index 00000000000..4534c850c37 --- /dev/null +++ b/seed/java-sdk/idempotency-headers/src/main/java/com/seed/idempotencyHeaders/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.idempotencyHeaders.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/idempotency-headers/src/main/java/com/seed/idempotencyHeaders/core/InputStreamRequestBody.java b/seed/java-sdk/idempotency-headers/src/main/java/com/seed/idempotencyHeaders/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..cf8281a2879 --- /dev/null +++ b/seed/java-sdk/idempotency-headers/src/main/java/com/seed/idempotencyHeaders/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.idempotencyHeaders.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/imdb/disable-required-property-builder-checks/src/main/java/com/seed/api/core/FileStream.java b/seed/java-sdk/imdb/disable-required-property-builder-checks/src/main/java/com/seed/api/core/FileStream.java new file mode 100644 index 00000000000..a71e7946986 --- /dev/null +++ b/seed/java-sdk/imdb/disable-required-property-builder-checks/src/main/java/com/seed/api/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/imdb/disable-required-property-builder-checks/src/main/java/com/seed/api/core/InputStreamRequestBody.java b/seed/java-sdk/imdb/disable-required-property-builder-checks/src/main/java/com/seed/api/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..fac2b602f6d --- /dev/null +++ b/seed/java-sdk/imdb/disable-required-property-builder-checks/src/main/java/com/seed/api/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/literal/src/main/java/com/seed/literal/core/FileStream.java b/seed/java-sdk/literal/src/main/java/com/seed/literal/core/FileStream.java new file mode 100644 index 00000000000..eac07a405a5 --- /dev/null +++ b/seed/java-sdk/literal/src/main/java/com/seed/literal/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.literal.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/literal/src/main/java/com/seed/literal/core/InputStreamRequestBody.java b/seed/java-sdk/literal/src/main/java/com/seed/literal/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..d581b1f0b9d --- /dev/null +++ b/seed/java-sdk/literal/src/main/java/com/seed/literal/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.literal.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/mixed-case/src/main/java/com/seed/mixedCase/core/FileStream.java b/seed/java-sdk/mixed-case/src/main/java/com/seed/mixedCase/core/FileStream.java new file mode 100644 index 00000000000..0663d7cd7d9 --- /dev/null +++ b/seed/java-sdk/mixed-case/src/main/java/com/seed/mixedCase/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.mixedCase.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/mixed-case/src/main/java/com/seed/mixedCase/core/InputStreamRequestBody.java b/seed/java-sdk/mixed-case/src/main/java/com/seed/mixedCase/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..a87d0f65b42 --- /dev/null +++ b/seed/java-sdk/mixed-case/src/main/java/com/seed/mixedCase/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.mixedCase.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/mixed-file-directory/src/main/java/com/seed/mixedFileDirectory/core/FileStream.java b/seed/java-sdk/mixed-file-directory/src/main/java/com/seed/mixedFileDirectory/core/FileStream.java new file mode 100644 index 00000000000..b10fd0397ac --- /dev/null +++ b/seed/java-sdk/mixed-file-directory/src/main/java/com/seed/mixedFileDirectory/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.mixedFileDirectory.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/mixed-file-directory/src/main/java/com/seed/mixedFileDirectory/core/InputStreamRequestBody.java b/seed/java-sdk/mixed-file-directory/src/main/java/com/seed/mixedFileDirectory/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..c1914e0787d --- /dev/null +++ b/seed/java-sdk/mixed-file-directory/src/main/java/com/seed/mixedFileDirectory/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.mixedFileDirectory.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/multi-line-docs/src/main/java/com/seed/multiLineDocs/core/FileStream.java b/seed/java-sdk/multi-line-docs/src/main/java/com/seed/multiLineDocs/core/FileStream.java new file mode 100644 index 00000000000..18a7417bbd3 --- /dev/null +++ b/seed/java-sdk/multi-line-docs/src/main/java/com/seed/multiLineDocs/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.multiLineDocs.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/multi-line-docs/src/main/java/com/seed/multiLineDocs/core/InputStreamRequestBody.java b/seed/java-sdk/multi-line-docs/src/main/java/com/seed/multiLineDocs/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..1faa318c03d --- /dev/null +++ b/seed/java-sdk/multi-line-docs/src/main/java/com/seed/multiLineDocs/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.multiLineDocs.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/multi-url-environment-no-default/src/main/java/com/seed/multiUrlEnvironmentNoDefault/core/FileStream.java b/seed/java-sdk/multi-url-environment-no-default/src/main/java/com/seed/multiUrlEnvironmentNoDefault/core/FileStream.java new file mode 100644 index 00000000000..eb28fca98f9 --- /dev/null +++ b/seed/java-sdk/multi-url-environment-no-default/src/main/java/com/seed/multiUrlEnvironmentNoDefault/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.multiUrlEnvironmentNoDefault.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/multi-url-environment-no-default/src/main/java/com/seed/multiUrlEnvironmentNoDefault/core/InputStreamRequestBody.java b/seed/java-sdk/multi-url-environment-no-default/src/main/java/com/seed/multiUrlEnvironmentNoDefault/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..652f60c2980 --- /dev/null +++ b/seed/java-sdk/multi-url-environment-no-default/src/main/java/com/seed/multiUrlEnvironmentNoDefault/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.multiUrlEnvironmentNoDefault.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/multi-url-environment/src/main/java/com/seed/multiUrlEnvironment/core/FileStream.java b/seed/java-sdk/multi-url-environment/src/main/java/com/seed/multiUrlEnvironment/core/FileStream.java new file mode 100644 index 00000000000..a1f30e608a0 --- /dev/null +++ b/seed/java-sdk/multi-url-environment/src/main/java/com/seed/multiUrlEnvironment/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.multiUrlEnvironment.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/multi-url-environment/src/main/java/com/seed/multiUrlEnvironment/core/InputStreamRequestBody.java b/seed/java-sdk/multi-url-environment/src/main/java/com/seed/multiUrlEnvironment/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..deb607f7179 --- /dev/null +++ b/seed/java-sdk/multi-url-environment/src/main/java/com/seed/multiUrlEnvironment/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.multiUrlEnvironment.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/no-environment/src/main/java/com/seed/noEnvironment/core/FileStream.java b/seed/java-sdk/no-environment/src/main/java/com/seed/noEnvironment/core/FileStream.java new file mode 100644 index 00000000000..7d46cb96fee --- /dev/null +++ b/seed/java-sdk/no-environment/src/main/java/com/seed/noEnvironment/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.noEnvironment.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/no-environment/src/main/java/com/seed/noEnvironment/core/InputStreamRequestBody.java b/seed/java-sdk/no-environment/src/main/java/com/seed/noEnvironment/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..bdf860f1ec9 --- /dev/null +++ b/seed/java-sdk/no-environment/src/main/java/com/seed/noEnvironment/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.noEnvironment.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/core/FileStream.java b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/core/FileStream.java new file mode 100644 index 00000000000..fd3e60a4b8f --- /dev/null +++ b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.oauthClientCredentialsDefault.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/core/InputStreamRequestBody.java b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..d53410dbc81 --- /dev/null +++ b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.oauthClientCredentialsDefault.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/core/FileStream.java b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/core/FileStream.java new file mode 100644 index 00000000000..feafd77e0ee --- /dev/null +++ b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.oauthClientCredentialsEnvironmentVariables.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/core/InputStreamRequestBody.java b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..9438a4caaf1 --- /dev/null +++ b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.oauthClientCredentialsEnvironmentVariables.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/core/FileStream.java b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/core/FileStream.java new file mode 100644 index 00000000000..da11a8674c0 --- /dev/null +++ b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.oauthClientCredentials.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/core/InputStreamRequestBody.java b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..d4ab08f764f --- /dev/null +++ b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.oauthClientCredentials.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/core/FileStream.java b/seed/java-sdk/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/core/FileStream.java new file mode 100644 index 00000000000..da11a8674c0 --- /dev/null +++ b/seed/java-sdk/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.oauthClientCredentials.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/core/InputStreamRequestBody.java b/seed/java-sdk/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..d4ab08f764f --- /dev/null +++ b/seed/java-sdk/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.oauthClientCredentials.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/object/src/main/java/com/seed/object/core/FileStream.java b/seed/java-sdk/object/src/main/java/com/seed/object/core/FileStream.java new file mode 100644 index 00000000000..eef28eef8bd --- /dev/null +++ b/seed/java-sdk/object/src/main/java/com/seed/object/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.object.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/object/src/main/java/com/seed/object/core/InputStreamRequestBody.java b/seed/java-sdk/object/src/main/java/com/seed/object/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..433d823fbed --- /dev/null +++ b/seed/java-sdk/object/src/main/java/com/seed/object/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.object.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/objects-with-imports/src/main/java/com/seed/objectsWithImports/core/FileStream.java b/seed/java-sdk/objects-with-imports/src/main/java/com/seed/objectsWithImports/core/FileStream.java new file mode 100644 index 00000000000..377c4177a2f --- /dev/null +++ b/seed/java-sdk/objects-with-imports/src/main/java/com/seed/objectsWithImports/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.objectsWithImports.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/objects-with-imports/src/main/java/com/seed/objectsWithImports/core/InputStreamRequestBody.java b/seed/java-sdk/objects-with-imports/src/main/java/com/seed/objectsWithImports/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..2457e338852 --- /dev/null +++ b/seed/java-sdk/objects-with-imports/src/main/java/com/seed/objectsWithImports/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.objectsWithImports.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/optional/src/main/java/com/seed/objectsWithImports/core/FileStream.java b/seed/java-sdk/optional/src/main/java/com/seed/objectsWithImports/core/FileStream.java new file mode 100644 index 00000000000..377c4177a2f --- /dev/null +++ b/seed/java-sdk/optional/src/main/java/com/seed/objectsWithImports/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.objectsWithImports.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/optional/src/main/java/com/seed/objectsWithImports/core/InputStreamRequestBody.java b/seed/java-sdk/optional/src/main/java/com/seed/objectsWithImports/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..2457e338852 --- /dev/null +++ b/seed/java-sdk/optional/src/main/java/com/seed/objectsWithImports/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.objectsWithImports.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/package-yml/src/main/java/com/seed/packageYml/core/FileStream.java b/seed/java-sdk/package-yml/src/main/java/com/seed/packageYml/core/FileStream.java new file mode 100644 index 00000000000..26fd80d57f7 --- /dev/null +++ b/seed/java-sdk/package-yml/src/main/java/com/seed/packageYml/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.packageYml.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/package-yml/src/main/java/com/seed/packageYml/core/InputStreamRequestBody.java b/seed/java-sdk/package-yml/src/main/java/com/seed/packageYml/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..076618ee718 --- /dev/null +++ b/seed/java-sdk/package-yml/src/main/java/com/seed/packageYml/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.packageYml.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/pagination/src/main/java/com/seed/pagination/core/FileStream.java b/seed/java-sdk/pagination/src/main/java/com/seed/pagination/core/FileStream.java new file mode 100644 index 00000000000..5dfc1dc8177 --- /dev/null +++ b/seed/java-sdk/pagination/src/main/java/com/seed/pagination/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.pagination.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/pagination/src/main/java/com/seed/pagination/core/InputStreamRequestBody.java b/seed/java-sdk/pagination/src/main/java/com/seed/pagination/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..f48e2de3c7e --- /dev/null +++ b/seed/java-sdk/pagination/src/main/java/com/seed/pagination/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.pagination.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/plain-text/src/main/java/com/seed/plainText/core/FileStream.java b/seed/java-sdk/plain-text/src/main/java/com/seed/plainText/core/FileStream.java new file mode 100644 index 00000000000..1797475fdb4 --- /dev/null +++ b/seed/java-sdk/plain-text/src/main/java/com/seed/plainText/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.plainText.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/plain-text/src/main/java/com/seed/plainText/core/InputStreamRequestBody.java b/seed/java-sdk/plain-text/src/main/java/com/seed/plainText/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..e134ec90541 --- /dev/null +++ b/seed/java-sdk/plain-text/src/main/java/com/seed/plainText/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.plainText.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/query-parameters/src/main/java/com/seed/queryParameters/core/FileStream.java b/seed/java-sdk/query-parameters/src/main/java/com/seed/queryParameters/core/FileStream.java new file mode 100644 index 00000000000..a2608b92203 --- /dev/null +++ b/seed/java-sdk/query-parameters/src/main/java/com/seed/queryParameters/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.queryParameters.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/query-parameters/src/main/java/com/seed/queryParameters/core/InputStreamRequestBody.java b/seed/java-sdk/query-parameters/src/main/java/com/seed/queryParameters/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..fc1420ad1ff --- /dev/null +++ b/seed/java-sdk/query-parameters/src/main/java/com/seed/queryParameters/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.queryParameters.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/reserved-keywords/src/main/java/com/seed/nurseryApi/core/FileStream.java b/seed/java-sdk/reserved-keywords/src/main/java/com/seed/nurseryApi/core/FileStream.java new file mode 100644 index 00000000000..1e6ffd27d02 --- /dev/null +++ b/seed/java-sdk/reserved-keywords/src/main/java/com/seed/nurseryApi/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.nurseryApi.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/reserved-keywords/src/main/java/com/seed/nurseryApi/core/InputStreamRequestBody.java b/seed/java-sdk/reserved-keywords/src/main/java/com/seed/nurseryApi/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..f8465567689 --- /dev/null +++ b/seed/java-sdk/reserved-keywords/src/main/java/com/seed/nurseryApi/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.nurseryApi.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/response-property/src/main/java/com/seed/responseProperty/core/FileStream.java b/seed/java-sdk/response-property/src/main/java/com/seed/responseProperty/core/FileStream.java new file mode 100644 index 00000000000..2cdfc9f8481 --- /dev/null +++ b/seed/java-sdk/response-property/src/main/java/com/seed/responseProperty/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.responseProperty.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/response-property/src/main/java/com/seed/responseProperty/core/InputStreamRequestBody.java b/seed/java-sdk/response-property/src/main/java/com/seed/responseProperty/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..6052667ad9a --- /dev/null +++ b/seed/java-sdk/response-property/src/main/java/com/seed/responseProperty/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.responseProperty.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/simple-fhir/src/main/java/com/seed/api/core/FileStream.java b/seed/java-sdk/simple-fhir/src/main/java/com/seed/api/core/FileStream.java new file mode 100644 index 00000000000..a71e7946986 --- /dev/null +++ b/seed/java-sdk/simple-fhir/src/main/java/com/seed/api/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/simple-fhir/src/main/java/com/seed/api/core/InputStreamRequestBody.java b/seed/java-sdk/simple-fhir/src/main/java/com/seed/api/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..fac2b602f6d --- /dev/null +++ b/seed/java-sdk/simple-fhir/src/main/java/com/seed/api/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.api.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/single-url-environment-default/src/main/java/com/seed/singleUrlEnvironmentDefault/core/FileStream.java b/seed/java-sdk/single-url-environment-default/src/main/java/com/seed/singleUrlEnvironmentDefault/core/FileStream.java new file mode 100644 index 00000000000..40b45228bdf --- /dev/null +++ b/seed/java-sdk/single-url-environment-default/src/main/java/com/seed/singleUrlEnvironmentDefault/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.singleUrlEnvironmentDefault.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/single-url-environment-default/src/main/java/com/seed/singleUrlEnvironmentDefault/core/InputStreamRequestBody.java b/seed/java-sdk/single-url-environment-default/src/main/java/com/seed/singleUrlEnvironmentDefault/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..348fe4ddb25 --- /dev/null +++ b/seed/java-sdk/single-url-environment-default/src/main/java/com/seed/singleUrlEnvironmentDefault/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.singleUrlEnvironmentDefault.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/single-url-environment-no-default/src/main/java/com/seed/singleUrlEnvironmentNoDefault/core/FileStream.java b/seed/java-sdk/single-url-environment-no-default/src/main/java/com/seed/singleUrlEnvironmentNoDefault/core/FileStream.java new file mode 100644 index 00000000000..52e9ce85c7a --- /dev/null +++ b/seed/java-sdk/single-url-environment-no-default/src/main/java/com/seed/singleUrlEnvironmentNoDefault/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.singleUrlEnvironmentNoDefault.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/single-url-environment-no-default/src/main/java/com/seed/singleUrlEnvironmentNoDefault/core/InputStreamRequestBody.java b/seed/java-sdk/single-url-environment-no-default/src/main/java/com/seed/singleUrlEnvironmentNoDefault/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..8cabf6df970 --- /dev/null +++ b/seed/java-sdk/single-url-environment-no-default/src/main/java/com/seed/singleUrlEnvironmentNoDefault/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.singleUrlEnvironmentNoDefault.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/streaming-parameter/src/main/java/com/seed/streaming/core/FileStream.java b/seed/java-sdk/streaming-parameter/src/main/java/com/seed/streaming/core/FileStream.java new file mode 100644 index 00000000000..4173800f0ce --- /dev/null +++ b/seed/java-sdk/streaming-parameter/src/main/java/com/seed/streaming/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.streaming.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/streaming-parameter/src/main/java/com/seed/streaming/core/InputStreamRequestBody.java b/seed/java-sdk/streaming-parameter/src/main/java/com/seed/streaming/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..5199168e084 --- /dev/null +++ b/seed/java-sdk/streaming-parameter/src/main/java/com/seed/streaming/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.streaming.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/streaming/src/main/java/com/seed/streaming/core/FileStream.java b/seed/java-sdk/streaming/src/main/java/com/seed/streaming/core/FileStream.java new file mode 100644 index 00000000000..4173800f0ce --- /dev/null +++ b/seed/java-sdk/streaming/src/main/java/com/seed/streaming/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.streaming.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/streaming/src/main/java/com/seed/streaming/core/InputStreamRequestBody.java b/seed/java-sdk/streaming/src/main/java/com/seed/streaming/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..5199168e084 --- /dev/null +++ b/seed/java-sdk/streaming/src/main/java/com/seed/streaming/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.streaming.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/trace/src/main/java/com/seed/trace/core/FileStream.java b/seed/java-sdk/trace/src/main/java/com/seed/trace/core/FileStream.java new file mode 100644 index 00000000000..b1e114d7ff5 --- /dev/null +++ b/seed/java-sdk/trace/src/main/java/com/seed/trace/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.trace.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/trace/src/main/java/com/seed/trace/core/InputStreamRequestBody.java b/seed/java-sdk/trace/src/main/java/com/seed/trace/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..cf42b5f8132 --- /dev/null +++ b/seed/java-sdk/trace/src/main/java/com/seed/trace/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.trace.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/undiscriminated-unions/src/main/java/com/seed/undiscriminatedUnions/core/FileStream.java b/seed/java-sdk/undiscriminated-unions/src/main/java/com/seed/undiscriminatedUnions/core/FileStream.java new file mode 100644 index 00000000000..064a6ea43f5 --- /dev/null +++ b/seed/java-sdk/undiscriminated-unions/src/main/java/com/seed/undiscriminatedUnions/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.undiscriminatedUnions.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/undiscriminated-unions/src/main/java/com/seed/undiscriminatedUnions/core/InputStreamRequestBody.java b/seed/java-sdk/undiscriminated-unions/src/main/java/com/seed/undiscriminatedUnions/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..8743e878ff8 --- /dev/null +++ b/seed/java-sdk/undiscriminated-unions/src/main/java/com/seed/undiscriminatedUnions/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.undiscriminatedUnions.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/unions/src/main/java/com/seed/unions/core/FileStream.java b/seed/java-sdk/unions/src/main/java/com/seed/unions/core/FileStream.java new file mode 100644 index 00000000000..274af29cb63 --- /dev/null +++ b/seed/java-sdk/unions/src/main/java/com/seed/unions/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.unions.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/unions/src/main/java/com/seed/unions/core/InputStreamRequestBody.java b/seed/java-sdk/unions/src/main/java/com/seed/unions/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..afca5d8a4f3 --- /dev/null +++ b/seed/java-sdk/unions/src/main/java/com/seed/unions/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.unions.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/unknown/src/main/java/com/seed/unknownAsAny/core/FileStream.java b/seed/java-sdk/unknown/src/main/java/com/seed/unknownAsAny/core/FileStream.java new file mode 100644 index 00000000000..490cce0f174 --- /dev/null +++ b/seed/java-sdk/unknown/src/main/java/com/seed/unknownAsAny/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.unknownAsAny.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/unknown/src/main/java/com/seed/unknownAsAny/core/InputStreamRequestBody.java b/seed/java-sdk/unknown/src/main/java/com/seed/unknownAsAny/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..f13b9c88637 --- /dev/null +++ b/seed/java-sdk/unknown/src/main/java/com/seed/unknownAsAny/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.unknownAsAny.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/validation/src/main/java/com/seed/validation/core/FileStream.java b/seed/java-sdk/validation/src/main/java/com/seed/validation/core/FileStream.java new file mode 100644 index 00000000000..e942938213f --- /dev/null +++ b/seed/java-sdk/validation/src/main/java/com/seed/validation/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.validation.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/validation/src/main/java/com/seed/validation/core/InputStreamRequestBody.java b/seed/java-sdk/validation/src/main/java/com/seed/validation/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..5c910f30632 --- /dev/null +++ b/seed/java-sdk/validation/src/main/java/com/seed/validation/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.validation.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/variables/src/main/java/com/seed/variables/core/FileStream.java b/seed/java-sdk/variables/src/main/java/com/seed/variables/core/FileStream.java new file mode 100644 index 00000000000..3055e439ffe --- /dev/null +++ b/seed/java-sdk/variables/src/main/java/com/seed/variables/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.variables.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/variables/src/main/java/com/seed/variables/core/InputStreamRequestBody.java b/seed/java-sdk/variables/src/main/java/com/seed/variables/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..cbd6d4a19b8 --- /dev/null +++ b/seed/java-sdk/variables/src/main/java/com/seed/variables/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.variables.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/version-no-default/src/main/java/com/seed/version/core/FileStream.java b/seed/java-sdk/version-no-default/src/main/java/com/seed/version/core/FileStream.java new file mode 100644 index 00000000000..07d530cfb6b --- /dev/null +++ b/seed/java-sdk/version-no-default/src/main/java/com/seed/version/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.version.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/version-no-default/src/main/java/com/seed/version/core/InputStreamRequestBody.java b/seed/java-sdk/version-no-default/src/main/java/com/seed/version/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..8de6f675890 --- /dev/null +++ b/seed/java-sdk/version-no-default/src/main/java/com/seed/version/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.version.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/version/src/main/java/com/seed/version/core/FileStream.java b/seed/java-sdk/version/src/main/java/com/seed/version/core/FileStream.java new file mode 100644 index 00000000000..07d530cfb6b --- /dev/null +++ b/seed/java-sdk/version/src/main/java/com/seed/version/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.version.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/version/src/main/java/com/seed/version/core/InputStreamRequestBody.java b/seed/java-sdk/version/src/main/java/com/seed/version/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..8de6f675890 --- /dev/null +++ b/seed/java-sdk/version/src/main/java/com/seed/version/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.version.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/websocket/src/main/java/com/seed/websocket/core/FileStream.java b/seed/java-sdk/websocket/src/main/java/com/seed/websocket/core/FileStream.java new file mode 100644 index 00000000000..dc62c9ca0fd --- /dev/null +++ b/seed/java-sdk/websocket/src/main/java/com/seed/websocket/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.websocket.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/websocket/src/main/java/com/seed/websocket/core/InputStreamRequestBody.java b/seed/java-sdk/websocket/src/main/java/com/seed/websocket/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..4eb209bd572 --- /dev/null +++ b/seed/java-sdk/websocket/src/main/java/com/seed/websocket/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.websocket.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/python-sdk/alias-extends/README.md b/seed/python-sdk/alias-extends/README.md index 7babd8fd7e5..abbcdf8edb9 100644 --- a/seed/python-sdk/alias-extends/README.md +++ b/seed/python-sdk/alias-extends/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_alias-extends)](https://pypi.python.org/pypi/fern_alias-extends) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_alias-extends ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/alias/README.md b/seed/python-sdk/alias/README.md index ee4785c9951..ff490795d1f 100644 --- a/seed/python-sdk/alias/README.md +++ b/seed/python-sdk/alias/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_alias)](https://pypi.python.org/pypi/fern_alias) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_alias ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/any-auth/README.md b/seed/python-sdk/any-auth/README.md index 02b7ca7c427..4fd1b748164 100644 --- a/seed/python-sdk/any-auth/README.md +++ b/seed/python-sdk/any-auth/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_any-auth)](https://pypi.python.org/pypi/fern_any-auth) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_any-auth ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/api-wide-base-path/README.md b/seed/python-sdk/api-wide-base-path/README.md index 58bc8350902..0b611435866 100644 --- a/seed/python-sdk/api-wide-base-path/README.md +++ b/seed/python-sdk/api-wide-base-path/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_api-wide-base-path)](https://pypi.python.org/pypi/fern_api-wide-base-path) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_api-wide-base-path ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/audiences/README.md b/seed/python-sdk/audiences/README.md index 13fd0498e7a..3f4911a9e1c 100644 --- a/seed/python-sdk/audiences/README.md +++ b/seed/python-sdk/audiences/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_audiences)](https://pypi.python.org/pypi/fern_audiences) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_audiences ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/auth-environment-variables/README.md b/seed/python-sdk/auth-environment-variables/README.md index 8195207a043..fae340f6e18 100644 --- a/seed/python-sdk/auth-environment-variables/README.md +++ b/seed/python-sdk/auth-environment-variables/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_auth-environment-variables)](https://pypi.python.org/pypi/fern_auth-environment-variables) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_auth-environment-variables ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/basic-auth-environment-variables/README.md b/seed/python-sdk/basic-auth-environment-variables/README.md index a8689204c64..ecadb40f1c0 100644 --- a/seed/python-sdk/basic-auth-environment-variables/README.md +++ b/seed/python-sdk/basic-auth-environment-variables/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_basic-auth-environment-variables)](https://pypi.python.org/pypi/fern_basic-auth-environment-variables) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_basic-auth-environment-variables ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/basic-auth/README.md b/seed/python-sdk/basic-auth/README.md index 8ad4670bf1f..dd6d795981c 100644 --- a/seed/python-sdk/basic-auth/README.md +++ b/seed/python-sdk/basic-auth/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_basic-auth)](https://pypi.python.org/pypi/fern_basic-auth) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_basic-auth ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/bearer-token-environment-variable/README.md b/seed/python-sdk/bearer-token-environment-variable/README.md index 6ad215ff0c8..2d3787763f5 100644 --- a/seed/python-sdk/bearer-token-environment-variable/README.md +++ b/seed/python-sdk/bearer-token-environment-variable/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_bearer-token-environment-variable)](https://pypi.python.org/pypi/fern_bearer-token-environment-variable) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_bearer-token-environment-variable ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/bytes/README.md b/seed/python-sdk/bytes/README.md index a7e9d875e0a..e4e7318abc1 100644 --- a/seed/python-sdk/bytes/README.md +++ b/seed/python-sdk/bytes/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_bytes)](https://pypi.python.org/pypi/fern_bytes) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_bytes ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/cross-package-type-names/README.md b/seed/python-sdk/cross-package-type-names/README.md index bb222e21aee..f36d3943407 100644 --- a/seed/python-sdk/cross-package-type-names/README.md +++ b/seed/python-sdk/cross-package-type-names/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_cross-package-type-names)](https://pypi.python.org/pypi/fern_cross-package-type-names) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_cross-package-type-names ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/custom-auth/README.md b/seed/python-sdk/custom-auth/README.md index bbb7f186277..284c6625d05 100644 --- a/seed/python-sdk/custom-auth/README.md +++ b/seed/python-sdk/custom-auth/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_custom-auth)](https://pypi.python.org/pypi/fern_custom-auth) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_custom-auth ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/enum/no-custom-config/README.md b/seed/python-sdk/enum/no-custom-config/README.md index 35b48cfcf09..73c99b6e8b7 100644 --- a/seed/python-sdk/enum/no-custom-config/README.md +++ b/seed/python-sdk/enum/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_enum)](https://pypi.python.org/pypi/fern_enum) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_enum ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/enum/real-enum-forward-compat/README.md b/seed/python-sdk/enum/real-enum-forward-compat/README.md index b7169b349ec..7ecd5bb0ac4 100644 --- a/seed/python-sdk/enum/real-enum-forward-compat/README.md +++ b/seed/python-sdk/enum/real-enum-forward-compat/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_enum)](https://pypi.python.org/pypi/fern_enum) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_enum ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/enum/real-enum/README.md b/seed/python-sdk/enum/real-enum/README.md index b7169b349ec..7ecd5bb0ac4 100644 --- a/seed/python-sdk/enum/real-enum/README.md +++ b/seed/python-sdk/enum/real-enum/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_enum)](https://pypi.python.org/pypi/fern_enum) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_enum ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/enum/strenum/README.md b/seed/python-sdk/enum/strenum/README.md index 35b48cfcf09..73c99b6e8b7 100644 --- a/seed/python-sdk/enum/strenum/README.md +++ b/seed/python-sdk/enum/strenum/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_enum)](https://pypi.python.org/pypi/fern_enum) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_enum ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/error-property/README.md b/seed/python-sdk/error-property/README.md index 1b035219bbe..4f3d171f053 100644 --- a/seed/python-sdk/error-property/README.md +++ b/seed/python-sdk/error-property/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_error-property)](https://pypi.python.org/pypi/fern_error-property) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_error-property ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/examples/client-filename/README.md b/seed/python-sdk/examples/client-filename/README.md index 53044c02098..781915b668a 100644 --- a/seed/python-sdk/examples/client-filename/README.md +++ b/seed/python-sdk/examples/client-filename/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_examples)](https://pypi.python.org/pypi/fern_examples) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_examples ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/examples/no-custom-config/README.md b/seed/python-sdk/examples/no-custom-config/README.md index de8334f50c0..cad5974b19b 100644 --- a/seed/python-sdk/examples/no-custom-config/README.md +++ b/seed/python-sdk/examples/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_examples)](https://pypi.python.org/pypi/fern_examples) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_examples ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/examples/readme/README.md b/seed/python-sdk/examples/readme/README.md index ac0c45ca56c..d3a896e187e 100644 --- a/seed/python-sdk/examples/readme/README.md +++ b/seed/python-sdk/examples/readme/README.md @@ -2,7 +2,7 @@ ![](https://www.fernapi.com) -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_examples)](https://pypi.python.org/pypi/fern_examples) The Seed Python library provides convenient access to the Seed API from Python. @@ -17,6 +17,10 @@ API reference documentation is available [here](https://www.docs.fernapi.com). pip install fern_examples ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/aliases_with_validation/README.md b/seed/python-sdk/exhaustive/aliases_with_validation/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/aliases_with_validation/README.md +++ b/seed/python-sdk/exhaustive/aliases_with_validation/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/aliases_without_validation/README.md b/seed/python-sdk/exhaustive/aliases_without_validation/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/aliases_without_validation/README.md +++ b/seed/python-sdk/exhaustive/aliases_without_validation/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/deps_with_min_python_version/README.md b/seed/python-sdk/exhaustive/deps_with_min_python_version/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/deps_with_min_python_version/README.md +++ b/seed/python-sdk/exhaustive/deps_with_min_python_version/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/extra_dependencies/README.md b/seed/python-sdk/exhaustive/extra_dependencies/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/extra_dependencies/README.md +++ b/seed/python-sdk/exhaustive/extra_dependencies/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/extra_dev_dependencies/README.md b/seed/python-sdk/exhaustive/extra_dev_dependencies/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/extra_dev_dependencies/README.md +++ b/seed/python-sdk/exhaustive/extra_dev_dependencies/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/five-second-timeout/README.md b/seed/python-sdk/exhaustive/five-second-timeout/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/five-second-timeout/README.md +++ b/seed/python-sdk/exhaustive/five-second-timeout/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/follow_redirects_by_default/README.md b/seed/python-sdk/exhaustive/follow_redirects_by_default/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/follow_redirects_by_default/README.md +++ b/seed/python-sdk/exhaustive/follow_redirects_by_default/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/improved_imports/README.md b/seed/python-sdk/exhaustive/improved_imports/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/improved_imports/README.md +++ b/seed/python-sdk/exhaustive/improved_imports/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/infinite-timeout/README.md b/seed/python-sdk/exhaustive/infinite-timeout/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/infinite-timeout/README.md +++ b/seed/python-sdk/exhaustive/infinite-timeout/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/inline_request_params/README.md b/seed/python-sdk/exhaustive/inline_request_params/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/inline_request_params/README.md +++ b/seed/python-sdk/exhaustive/inline_request_params/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/no-custom-config/README.md b/seed/python-sdk/exhaustive/no-custom-config/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/README.md +++ b/seed/python-sdk/exhaustive/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/pydantic-extra-fields/README.md b/seed/python-sdk/exhaustive/pydantic-extra-fields/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/pydantic-extra-fields/README.md +++ b/seed/python-sdk/exhaustive/pydantic-extra-fields/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/README.md b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/README.md +++ b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/pydantic-v1/README.md b/seed/python-sdk/exhaustive/pydantic-v1/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1/README.md +++ b/seed/python-sdk/exhaustive/pydantic-v1/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/pyproject_extras/README.md b/seed/python-sdk/exhaustive/pyproject_extras/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/pyproject_extras/README.md +++ b/seed/python-sdk/exhaustive/pyproject_extras/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/skip-pydantic-validation/README.md b/seed/python-sdk/exhaustive/skip-pydantic-validation/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/skip-pydantic-validation/README.md +++ b/seed/python-sdk/exhaustive/skip-pydantic-validation/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/exhaustive/union-utils/README.md b/seed/python-sdk/exhaustive/union-utils/README.md index 531065849a0..4e91415b2ee 100644 --- a/seed/python-sdk/exhaustive/union-utils/README.md +++ b/seed/python-sdk/exhaustive/union-utils/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_exhaustive)](https://pypi.python.org/pypi/fern_exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/extends/README.md b/seed/python-sdk/extends/README.md index 3281f0f7eee..df5c03d2c53 100644 --- a/seed/python-sdk/extends/README.md +++ b/seed/python-sdk/extends/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_extends)](https://pypi.python.org/pypi/fern_extends) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_extends ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/extra-properties/README.md b/seed/python-sdk/extra-properties/README.md index 0a5e2ad968b..542ec8560a0 100644 --- a/seed/python-sdk/extra-properties/README.md +++ b/seed/python-sdk/extra-properties/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_extra-properties)](https://pypi.python.org/pypi/fern_extra-properties) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_extra-properties ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/file-download/README.md b/seed/python-sdk/file-download/README.md index 224406af1ef..d2ecf74a149 100644 --- a/seed/python-sdk/file-download/README.md +++ b/seed/python-sdk/file-download/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_file-download)](https://pypi.python.org/pypi/fern_file-download) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_file-download ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/file-upload/README.md b/seed/python-sdk/file-upload/README.md index 401547e2070..15ab01cba62 100644 --- a/seed/python-sdk/file-upload/README.md +++ b/seed/python-sdk/file-upload/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_file-upload)](https://pypi.python.org/pypi/fern_file-upload) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_file-upload ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/folders/README.md b/seed/python-sdk/folders/README.md index 583c639d51b..9bb5d578cad 100644 --- a/seed/python-sdk/folders/README.md +++ b/seed/python-sdk/folders/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_folders)](https://pypi.python.org/pypi/fern_folders) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_folders ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/grpc-proto-exhaustive/README.md b/seed/python-sdk/grpc-proto-exhaustive/README.md index 59ef0440a0f..c01d91950d8 100644 --- a/seed/python-sdk/grpc-proto-exhaustive/README.md +++ b/seed/python-sdk/grpc-proto-exhaustive/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_grpc-proto-exhaustive)](https://pypi.python.org/pypi/fern_grpc-proto-exhaustive) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_grpc-proto-exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/grpc-proto/README.md b/seed/python-sdk/grpc-proto/README.md index 99d273e3e1e..7eeec27f24a 100644 --- a/seed/python-sdk/grpc-proto/README.md +++ b/seed/python-sdk/grpc-proto/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_grpc-proto)](https://pypi.python.org/pypi/fern_grpc-proto) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_grpc-proto ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/idempotency-headers/README.md b/seed/python-sdk/idempotency-headers/README.md index 95b6bd273fe..0a580c439b8 100644 --- a/seed/python-sdk/idempotency-headers/README.md +++ b/seed/python-sdk/idempotency-headers/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_idempotency-headers)](https://pypi.python.org/pypi/fern_idempotency-headers) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_idempotency-headers ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/imdb/README.md b/seed/python-sdk/imdb/README.md index 0362b0e47f2..87bcf4c45ad 100644 --- a/seed/python-sdk/imdb/README.md +++ b/seed/python-sdk/imdb/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_imdb)](https://pypi.python.org/pypi/fern_imdb) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_imdb ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/literal/no-custom-config/README.md b/seed/python-sdk/literal/no-custom-config/README.md index ce0fc59758f..1b4d6676864 100644 --- a/seed/python-sdk/literal/no-custom-config/README.md +++ b/seed/python-sdk/literal/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_literal)](https://pypi.python.org/pypi/fern_literal) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_literal ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/literal/use_typeddict_requests/README.md b/seed/python-sdk/literal/use_typeddict_requests/README.md index ce0fc59758f..1b4d6676864 100644 --- a/seed/python-sdk/literal/use_typeddict_requests/README.md +++ b/seed/python-sdk/literal/use_typeddict_requests/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_literal)](https://pypi.python.org/pypi/fern_literal) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_literal ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/mixed-case/README.md b/seed/python-sdk/mixed-case/README.md index 0d9b08e3b91..261ab22f7a3 100644 --- a/seed/python-sdk/mixed-case/README.md +++ b/seed/python-sdk/mixed-case/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_mixed-case)](https://pypi.python.org/pypi/fern_mixed-case) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_mixed-case ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/mixed-file-directory/README.md b/seed/python-sdk/mixed-file-directory/README.md index 57ed169a1fe..5929b10beda 100644 --- a/seed/python-sdk/mixed-file-directory/README.md +++ b/seed/python-sdk/mixed-file-directory/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_mixed-file-directory)](https://pypi.python.org/pypi/fern_mixed-file-directory) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_mixed-file-directory ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/multi-line-docs/README.md b/seed/python-sdk/multi-line-docs/README.md index 7ee7e396b1d..7ac89b7be78 100644 --- a/seed/python-sdk/multi-line-docs/README.md +++ b/seed/python-sdk/multi-line-docs/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_multi-line-docs)](https://pypi.python.org/pypi/fern_multi-line-docs) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_multi-line-docs ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/multi-url-environment-no-default/README.md b/seed/python-sdk/multi-url-environment-no-default/README.md index e872dc25204..50ed5ad105e 100644 --- a/seed/python-sdk/multi-url-environment-no-default/README.md +++ b/seed/python-sdk/multi-url-environment-no-default/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_multi-url-environment-no-default)](https://pypi.python.org/pypi/fern_multi-url-environment-no-default) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_multi-url-environment-no-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/multi-url-environment/README.md b/seed/python-sdk/multi-url-environment/README.md index f55f46b6bcd..1f15bc5981f 100644 --- a/seed/python-sdk/multi-url-environment/README.md +++ b/seed/python-sdk/multi-url-environment/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_multi-url-environment)](https://pypi.python.org/pypi/fern_multi-url-environment) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_multi-url-environment ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/no-environment/README.md b/seed/python-sdk/no-environment/README.md index fc5c801d0a9..b94b5584bfc 100644 --- a/seed/python-sdk/no-environment/README.md +++ b/seed/python-sdk/no-environment/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_no-environment)](https://pypi.python.org/pypi/fern_no-environment) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_no-environment ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/oauth-client-credentials-default/README.md b/seed/python-sdk/oauth-client-credentials-default/README.md index 9321878e1df..37b1bf26f90 100644 --- a/seed/python-sdk/oauth-client-credentials-default/README.md +++ b/seed/python-sdk/oauth-client-credentials-default/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_oauth-client-credentials-default)](https://pypi.python.org/pypi/fern_oauth-client-credentials-default) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_oauth-client-credentials-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/README.md b/seed/python-sdk/oauth-client-credentials-environment-variables/README.md index 464e6324a55..6a4cd70a423 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/README.md +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_oauth-client-credentials-environment-variables)](https://pypi.python.org/pypi/fern_oauth-client-credentials-environment-variables) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_oauth-client-credentials-environment-variables ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/README.md b/seed/python-sdk/oauth-client-credentials-nested-root/README.md index e4f861fb6c3..f81030e7344 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/README.md +++ b/seed/python-sdk/oauth-client-credentials-nested-root/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_oauth-client-credentials-nested-root)](https://pypi.python.org/pypi/fern_oauth-client-credentials-nested-root) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_oauth-client-credentials-nested-root ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/oauth-client-credentials/README.md b/seed/python-sdk/oauth-client-credentials/README.md index 054633fb7fb..f901e36593c 100644 --- a/seed/python-sdk/oauth-client-credentials/README.md +++ b/seed/python-sdk/oauth-client-credentials/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_oauth-client-credentials)](https://pypi.python.org/pypi/fern_oauth-client-credentials) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_oauth-client-credentials ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/optional/README.md b/seed/python-sdk/optional/README.md index 4e7efbb3832..01678c7ac6c 100644 --- a/seed/python-sdk/optional/README.md +++ b/seed/python-sdk/optional/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_optional)](https://pypi.python.org/pypi/fern_optional) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_optional ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/package-yml/README.md b/seed/python-sdk/package-yml/README.md index 72ef908178f..3d50c866a90 100644 --- a/seed/python-sdk/package-yml/README.md +++ b/seed/python-sdk/package-yml/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_package-yml)](https://pypi.python.org/pypi/fern_package-yml) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_package-yml ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/pagination/README.md b/seed/python-sdk/pagination/README.md index d8808a9c929..50667345cdb 100644 --- a/seed/python-sdk/pagination/README.md +++ b/seed/python-sdk/pagination/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_pagination)](https://pypi.python.org/pypi/fern_pagination) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_pagination ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/plain-text/README.md b/seed/python-sdk/plain-text/README.md index 973dc783c29..3fe17c2a460 100644 --- a/seed/python-sdk/plain-text/README.md +++ b/seed/python-sdk/plain-text/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_plain-text)](https://pypi.python.org/pypi/fern_plain-text) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_plain-text ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/query-parameters/README.md b/seed/python-sdk/query-parameters/README.md index 2a8c77b1819..b3aca21c47f 100644 --- a/seed/python-sdk/query-parameters/README.md +++ b/seed/python-sdk/query-parameters/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_query-parameters)](https://pypi.python.org/pypi/fern_query-parameters) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_query-parameters ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/reserved-keywords/README.md b/seed/python-sdk/reserved-keywords/README.md index 5fb593ad81b..c6153740a30 100644 --- a/seed/python-sdk/reserved-keywords/README.md +++ b/seed/python-sdk/reserved-keywords/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_reserved-keywords)](https://pypi.python.org/pypi/fern_reserved-keywords) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_reserved-keywords ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/response-property/README.md b/seed/python-sdk/response-property/README.md index 2a2c5f5b853..fcac506271e 100644 --- a/seed/python-sdk/response-property/README.md +++ b/seed/python-sdk/response-property/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_response-property)](https://pypi.python.org/pypi/fern_response-property) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_response-property ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/simple-fhir/README.md b/seed/python-sdk/simple-fhir/README.md index b49eda40852..6b89f0eb3e8 100644 --- a/seed/python-sdk/simple-fhir/README.md +++ b/seed/python-sdk/simple-fhir/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_simple-fhir)](https://pypi.python.org/pypi/fern_simple-fhir) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_simple-fhir ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/single-url-environment-default/README.md b/seed/python-sdk/single-url-environment-default/README.md index 478efe7d50a..b4d39020c07 100644 --- a/seed/python-sdk/single-url-environment-default/README.md +++ b/seed/python-sdk/single-url-environment-default/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_single-url-environment-default)](https://pypi.python.org/pypi/fern_single-url-environment-default) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_single-url-environment-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/single-url-environment-no-default/README.md b/seed/python-sdk/single-url-environment-no-default/README.md index 788e16708da..562a7e926e4 100644 --- a/seed/python-sdk/single-url-environment-no-default/README.md +++ b/seed/python-sdk/single-url-environment-no-default/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_single-url-environment-no-default)](https://pypi.python.org/pypi/fern_single-url-environment-no-default) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_single-url-environment-no-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/streaming-parameter/README.md b/seed/python-sdk/streaming-parameter/README.md index 3449498133d..0817cb2b7a1 100644 --- a/seed/python-sdk/streaming-parameter/README.md +++ b/seed/python-sdk/streaming-parameter/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_streaming-parameter)](https://pypi.python.org/pypi/fern_streaming-parameter) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_streaming-parameter ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/streaming/no-custom-config/README.md b/seed/python-sdk/streaming/no-custom-config/README.md index b9a1b18cac0..563b4c54e85 100644 --- a/seed/python-sdk/streaming/no-custom-config/README.md +++ b/seed/python-sdk/streaming/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_streaming)](https://pypi.python.org/pypi/fern_streaming) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_streaming ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/streaming/skip-pydantic-validation/README.md b/seed/python-sdk/streaming/skip-pydantic-validation/README.md index b9a1b18cac0..563b4c54e85 100644 --- a/seed/python-sdk/streaming/skip-pydantic-validation/README.md +++ b/seed/python-sdk/streaming/skip-pydantic-validation/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_streaming)](https://pypi.python.org/pypi/fern_streaming) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_streaming ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/trace/README.md b/seed/python-sdk/trace/README.md index 83d8aea3ecc..bfd6b389658 100644 --- a/seed/python-sdk/trace/README.md +++ b/seed/python-sdk/trace/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_trace)](https://pypi.python.org/pypi/fern_trace) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_trace ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/undiscriminated-unions/README.md b/seed/python-sdk/undiscriminated-unions/README.md index 3243f75e738..6336d306f3f 100644 --- a/seed/python-sdk/undiscriminated-unions/README.md +++ b/seed/python-sdk/undiscriminated-unions/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_undiscriminated-unions)](https://pypi.python.org/pypi/fern_undiscriminated-unions) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_undiscriminated-unions ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/unions/no-custom-config/README.md b/seed/python-sdk/unions/no-custom-config/README.md index 70c6b428b0a..fcff1c12e43 100644 --- a/seed/python-sdk/unions/no-custom-config/README.md +++ b/seed/python-sdk/unions/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_unions)](https://pypi.python.org/pypi/fern_unions) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_unions ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/unions/union-naming-v1/README.md b/seed/python-sdk/unions/union-naming-v1/README.md index 70c6b428b0a..fcff1c12e43 100644 --- a/seed/python-sdk/unions/union-naming-v1/README.md +++ b/seed/python-sdk/unions/union-naming-v1/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_unions)](https://pypi.python.org/pypi/fern_unions) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_unions ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/unions/union-utils/README.md b/seed/python-sdk/unions/union-utils/README.md index 70c6b428b0a..fcff1c12e43 100644 --- a/seed/python-sdk/unions/union-utils/README.md +++ b/seed/python-sdk/unions/union-utils/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_unions)](https://pypi.python.org/pypi/fern_unions) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_unions ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/unknown/README.md b/seed/python-sdk/unknown/README.md index 240e7037f48..b086a5027aa 100644 --- a/seed/python-sdk/unknown/README.md +++ b/seed/python-sdk/unknown/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_unknown)](https://pypi.python.org/pypi/fern_unknown) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_unknown ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/validation/no-custom-config/README.md b/seed/python-sdk/validation/no-custom-config/README.md index 04c1997e0b2..349526c4838 100644 --- a/seed/python-sdk/validation/no-custom-config/README.md +++ b/seed/python-sdk/validation/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_validation)](https://pypi.python.org/pypi/fern_validation) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_validation ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/validation/with-defaults/README.md b/seed/python-sdk/validation/with-defaults/README.md index 04c1997e0b2..349526c4838 100644 --- a/seed/python-sdk/validation/with-defaults/README.md +++ b/seed/python-sdk/validation/with-defaults/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_validation)](https://pypi.python.org/pypi/fern_validation) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_validation ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/variables/README.md b/seed/python-sdk/variables/README.md index 26087537f44..40befb3d156 100644 --- a/seed/python-sdk/variables/README.md +++ b/seed/python-sdk/variables/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_variables)](https://pypi.python.org/pypi/fern_variables) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_variables ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/version-no-default/README.md b/seed/python-sdk/version-no-default/README.md index af412a388f8..29fb7151af5 100644 --- a/seed/python-sdk/version-no-default/README.md +++ b/seed/python-sdk/version-no-default/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_version-no-default)](https://pypi.python.org/pypi/fern_version-no-default) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_version-no-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/python-sdk/version/README.md b/seed/python-sdk/version/README.md index 5c32b8fc0de..59669145e2a 100644 --- a/seed/python-sdk/version/README.md +++ b/seed/python-sdk/version/README.md @@ -1,6 +1,6 @@ # Seed Python Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) [![pypi](https://img.shields.io/pypi/v/fern_version)](https://pypi.python.org/pypi/fern_version) The Seed Python library provides convenient access to the Seed API from Python. @@ -11,6 +11,10 @@ The Seed Python library provides convenient access to the Seed API from Python. pip install fern_version ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/alias-extends/README.md b/seed/ts-sdk/alias-extends/README.md index c7864327147..2b5679f54cd 100644 --- a/seed/ts-sdk/alias-extends/README.md +++ b/seed/ts-sdk/alias-extends/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/alias-extends)](https://www.npmjs.com/package/@fern/alias-extends) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/alias-extends ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/alias/README.md b/seed/ts-sdk/alias/README.md index 8320f0bd0e1..8f1df7fa6f9 100644 --- a/seed/ts-sdk/alias/README.md +++ b/seed/ts-sdk/alias/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/alias)](https://www.npmjs.com/package/@fern/alias) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/alias ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/any-auth/README.md b/seed/ts-sdk/any-auth/README.md index 3fe942a5a74..57b7d39ed2a 100644 --- a/seed/ts-sdk/any-auth/README.md +++ b/seed/ts-sdk/any-auth/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/any-auth)](https://www.npmjs.com/package/@fern/any-auth) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/any-auth ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/api-wide-base-path/README.md b/seed/ts-sdk/api-wide-base-path/README.md index 28c0f191eee..7e7ee0b9697 100644 --- a/seed/ts-sdk/api-wide-base-path/README.md +++ b/seed/ts-sdk/api-wide-base-path/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/api-wide-base-path)](https://www.npmjs.com/package/@fern/api-wide-base-path) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/api-wide-base-path ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/audiences/no-custom-config/README.md b/seed/ts-sdk/audiences/no-custom-config/README.md index 79395aa20b5..cc5e700c7c7 100644 --- a/seed/ts-sdk/audiences/no-custom-config/README.md +++ b/seed/ts-sdk/audiences/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/audiences)](https://www.npmjs.com/package/@fern/audiences) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/audiences ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/audiences/with-partner-audience/README.md b/seed/ts-sdk/audiences/with-partner-audience/README.md index 6c8fb52fbf4..6eb8909789f 100644 --- a/seed/ts-sdk/audiences/with-partner-audience/README.md +++ b/seed/ts-sdk/audiences/with-partner-audience/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/audiences)](https://www.npmjs.com/package/@fern/audiences) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/audiences ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/auth-environment-variables/README.md b/seed/ts-sdk/auth-environment-variables/README.md index 6dff922d13e..c71bfcf7ef3 100644 --- a/seed/ts-sdk/auth-environment-variables/README.md +++ b/seed/ts-sdk/auth-environment-variables/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/auth-environment-variables)](https://www.npmjs.com/package/@fern/auth-environment-variables) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/auth-environment-variables ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/basic-auth-environment-variables/README.md b/seed/ts-sdk/basic-auth-environment-variables/README.md index 03d2e043bcc..268c94a7e0d 100644 --- a/seed/ts-sdk/basic-auth-environment-variables/README.md +++ b/seed/ts-sdk/basic-auth-environment-variables/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/basic-auth-environment-variables)](https://www.npmjs.com/package/@fern/basic-auth-environment-variables) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/basic-auth-environment-variables ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/basic-auth/README.md b/seed/ts-sdk/basic-auth/README.md index e30084cc995..5fc892e9fdc 100644 --- a/seed/ts-sdk/basic-auth/README.md +++ b/seed/ts-sdk/basic-auth/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/basic-auth)](https://www.npmjs.com/package/@fern/basic-auth) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/basic-auth ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/bearer-token-environment-variable/README.md b/seed/ts-sdk/bearer-token-environment-variable/README.md index 4871e4d7341..f58f263348e 100644 --- a/seed/ts-sdk/bearer-token-environment-variable/README.md +++ b/seed/ts-sdk/bearer-token-environment-variable/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/bearer-token-environment-variable)](https://www.npmjs.com/package/@fern/bearer-token-environment-variable) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/bearer-token-environment-variable ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/cross-package-type-names/README.md b/seed/ts-sdk/cross-package-type-names/README.md index 5dfee6ce708..a5a0f58da79 100644 --- a/seed/ts-sdk/cross-package-type-names/README.md +++ b/seed/ts-sdk/cross-package-type-names/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/cross-package-type-names)](https://www.npmjs.com/package/@fern/cross-package-type-names) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/cross-package-type-names ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/custom-auth/README.md b/seed/ts-sdk/custom-auth/README.md index 4f18830cf13..71377250536 100644 --- a/seed/ts-sdk/custom-auth/README.md +++ b/seed/ts-sdk/custom-auth/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/custom-auth)](https://www.npmjs.com/package/@fern/custom-auth) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/custom-auth ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/enum/README.md b/seed/ts-sdk/enum/README.md index 053848b2a80..fd18f064854 100644 --- a/seed/ts-sdk/enum/README.md +++ b/seed/ts-sdk/enum/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/enum)](https://www.npmjs.com/package/@fern/enum) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/enum ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/error-property/union-utils/README.md b/seed/ts-sdk/error-property/union-utils/README.md index d361a96ba7a..a7f221c6e12 100644 --- a/seed/ts-sdk/error-property/union-utils/README.md +++ b/seed/ts-sdk/error-property/union-utils/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/error-property)](https://www.npmjs.com/package/@fern/error-property) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/error-property ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/examples/examples-with-api-reference/README.md b/seed/ts-sdk/examples/examples-with-api-reference/README.md index 002c6ad5e15..edf3f931a2f 100644 --- a/seed/ts-sdk/examples/examples-with-api-reference/README.md +++ b/seed/ts-sdk/examples/examples-with-api-reference/README.md @@ -2,7 +2,7 @@ ![](https://www.fernapi.com) -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/examples)](https://www.npmjs.com/package/@fern/examples) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -17,6 +17,10 @@ API reference documentation is available [here](https://www.docs.fernapi.com). npm i -s @fern/examples ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/examples/retain-original-casing/README.md b/seed/ts-sdk/examples/retain-original-casing/README.md index 55143788987..7e5b8e7e412 100644 --- a/seed/ts-sdk/examples/retain-original-casing/README.md +++ b/seed/ts-sdk/examples/retain-original-casing/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/examples)](https://www.npmjs.com/package/@fern/examples) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/examples ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/README.md b/seed/ts-sdk/exhaustive/allow-extra-fields/README.md index d4a1bcd266e..754c6d11a9c 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/README.md +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/exhaustive/bigint/README.md b/seed/ts-sdk/exhaustive/bigint/README.md index d4a1bcd266e..754c6d11a9c 100644 --- a/seed/ts-sdk/exhaustive/bigint/README.md +++ b/seed/ts-sdk/exhaustive/bigint/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/exhaustive/bundle/README.md b/seed/ts-sdk/exhaustive/bundle/README.md index 51ccf90a54d..7af6bb6f5c1 100644 --- a/seed/ts-sdk/exhaustive/bundle/README.md +++ b/seed/ts-sdk/exhaustive/bundle/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/exhaustive/custom-package-json/README.md b/seed/ts-sdk/exhaustive/custom-package-json/README.md index 51ccf90a54d..7af6bb6f5c1 100644 --- a/seed/ts-sdk/exhaustive/custom-package-json/README.md +++ b/seed/ts-sdk/exhaustive/custom-package-json/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/README.md b/seed/ts-sdk/exhaustive/dev-dependencies/README.md index 51ccf90a54d..7af6bb6f5c1 100644 --- a/seed/ts-sdk/exhaustive/dev-dependencies/README.md +++ b/seed/ts-sdk/exhaustive/dev-dependencies/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/exhaustive/jsr/README.md b/seed/ts-sdk/exhaustive/jsr/README.md index d4a1bcd266e..754c6d11a9c 100644 --- a/seed/ts-sdk/exhaustive/jsr/README.md +++ b/seed/ts-sdk/exhaustive/jsr/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/exhaustive/no-custom-config/README.md b/seed/ts-sdk/exhaustive/no-custom-config/README.md index d4a1bcd266e..754c6d11a9c 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/README.md +++ b/seed/ts-sdk/exhaustive/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/README.md b/seed/ts-sdk/exhaustive/retain-original-casing/README.md index d4a1bcd266e..754c6d11a9c 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/README.md +++ b/seed/ts-sdk/exhaustive/retain-original-casing/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/exhaustive)](https://www.npmjs.com/package/@fern/exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/extends/README.md b/seed/ts-sdk/extends/README.md index 570f2895e5f..58df1f481f1 100644 --- a/seed/ts-sdk/extends/README.md +++ b/seed/ts-sdk/extends/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/extends)](https://www.npmjs.com/package/@fern/extends) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/extends ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/extra-properties/README.md b/seed/ts-sdk/extra-properties/README.md index 7c6101c3b84..e63ba0cd188 100644 --- a/seed/ts-sdk/extra-properties/README.md +++ b/seed/ts-sdk/extra-properties/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/extra-properties)](https://www.npmjs.com/package/@fern/extra-properties) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/extra-properties ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/file-download/file-download-reponse-headers/README.md b/seed/ts-sdk/file-download/file-download-reponse-headers/README.md index 2a31954d63a..2c953a6b737 100644 --- a/seed/ts-sdk/file-download/file-download-reponse-headers/README.md +++ b/seed/ts-sdk/file-download/file-download-reponse-headers/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/file-download)](https://www.npmjs.com/package/@fern/file-download) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/file-download ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/file-download/no-custom-config/README.md b/seed/ts-sdk/file-download/no-custom-config/README.md index 2a31954d63a..2c953a6b737 100644 --- a/seed/ts-sdk/file-download/no-custom-config/README.md +++ b/seed/ts-sdk/file-download/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/file-download)](https://www.npmjs.com/package/@fern/file-download) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/file-download ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/file-upload/no-custom-config/README.md b/seed/ts-sdk/file-upload/no-custom-config/README.md index 0cf6da365fd..9e9b45f8a36 100644 --- a/seed/ts-sdk/file-upload/no-custom-config/README.md +++ b/seed/ts-sdk/file-upload/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/file-upload)](https://www.npmjs.com/package/@fern/file-upload) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/file-upload ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/file-upload/wrap-file-properties/README.md b/seed/ts-sdk/file-upload/wrap-file-properties/README.md index c666f2fe05c..c66604eaf63 100644 --- a/seed/ts-sdk/file-upload/wrap-file-properties/README.md +++ b/seed/ts-sdk/file-upload/wrap-file-properties/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/file-upload)](https://www.npmjs.com/package/@fern/file-upload) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/file-upload ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/folders/README.md b/seed/ts-sdk/folders/README.md index e480668d90f..4a1a52989b1 100644 --- a/seed/ts-sdk/folders/README.md +++ b/seed/ts-sdk/folders/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/folders)](https://www.npmjs.com/package/@fern/folders) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/folders ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/grpc-proto-exhaustive/README.md b/seed/ts-sdk/grpc-proto-exhaustive/README.md index deedce13d15..62b9e1f99b1 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/README.md +++ b/seed/ts-sdk/grpc-proto-exhaustive/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/grpc-proto-exhaustive)](https://www.npmjs.com/package/@fern/grpc-proto-exhaustive) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/grpc-proto-exhaustive ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/idempotency-headers/README.md b/seed/ts-sdk/idempotency-headers/README.md index c24f107fdf2..acd376bb4ef 100644 --- a/seed/ts-sdk/idempotency-headers/README.md +++ b/seed/ts-sdk/idempotency-headers/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/idempotency-headers)](https://www.npmjs.com/package/@fern/idempotency-headers) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/idempotency-headers ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/imdb/no-custom-config/README.md b/seed/ts-sdk/imdb/no-custom-config/README.md index 19724990cba..27e65f659b1 100644 --- a/seed/ts-sdk/imdb/no-custom-config/README.md +++ b/seed/ts-sdk/imdb/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/imdb)](https://www.npmjs.com/package/@fern/imdb) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/imdb ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/imdb/noScripts/README.md b/seed/ts-sdk/imdb/noScripts/README.md index 419aa2505e6..ff525c92e3d 100644 --- a/seed/ts-sdk/imdb/noScripts/README.md +++ b/seed/ts-sdk/imdb/noScripts/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/imdb)](https://www.npmjs.com/package/@fern/imdb) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/imdb ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/imdb/omit-undefined/README.md b/seed/ts-sdk/imdb/omit-undefined/README.md index 19724990cba..27e65f659b1 100644 --- a/seed/ts-sdk/imdb/omit-undefined/README.md +++ b/seed/ts-sdk/imdb/omit-undefined/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/imdb)](https://www.npmjs.com/package/@fern/imdb) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/imdb ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/literal/README.md b/seed/ts-sdk/literal/README.md index 8466d40cba1..5f84c2bb49e 100644 --- a/seed/ts-sdk/literal/README.md +++ b/seed/ts-sdk/literal/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/literal)](https://www.npmjs.com/package/@fern/literal) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/literal ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/mixed-case/no-custom-config/README.md b/seed/ts-sdk/mixed-case/no-custom-config/README.md index 770faf2252c..09e6bf22adb 100644 --- a/seed/ts-sdk/mixed-case/no-custom-config/README.md +++ b/seed/ts-sdk/mixed-case/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/mixed-case)](https://www.npmjs.com/package/@fern/mixed-case) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/mixed-case ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/mixed-case/retain-original-casing/README.md b/seed/ts-sdk/mixed-case/retain-original-casing/README.md index 770faf2252c..09e6bf22adb 100644 --- a/seed/ts-sdk/mixed-case/retain-original-casing/README.md +++ b/seed/ts-sdk/mixed-case/retain-original-casing/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/mixed-case)](https://www.npmjs.com/package/@fern/mixed-case) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/mixed-case ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/mixed-file-directory/README.md b/seed/ts-sdk/mixed-file-directory/README.md index f9f203d5fbd..fb409d84367 100644 --- a/seed/ts-sdk/mixed-file-directory/README.md +++ b/seed/ts-sdk/mixed-file-directory/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/mixed-file-directory)](https://www.npmjs.com/package/@fern/mixed-file-directory) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/mixed-file-directory ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/multi-line-docs/README.md b/seed/ts-sdk/multi-line-docs/README.md index 869115e9804..1f78ad274fa 100644 --- a/seed/ts-sdk/multi-line-docs/README.md +++ b/seed/ts-sdk/multi-line-docs/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/multi-line-docs)](https://www.npmjs.com/package/@fern/multi-line-docs) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/multi-line-docs ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/multi-url-environment-no-default/README.md b/seed/ts-sdk/multi-url-environment-no-default/README.md index b8af3a75a0e..c425347b1bd 100644 --- a/seed/ts-sdk/multi-url-environment-no-default/README.md +++ b/seed/ts-sdk/multi-url-environment-no-default/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/multi-url-environment-no-default)](https://www.npmjs.com/package/@fern/multi-url-environment-no-default) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/multi-url-environment-no-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/multi-url-environment/README.md b/seed/ts-sdk/multi-url-environment/README.md index f31761150c9..bcf9626e055 100644 --- a/seed/ts-sdk/multi-url-environment/README.md +++ b/seed/ts-sdk/multi-url-environment/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/multi-url-environment)](https://www.npmjs.com/package/@fern/multi-url-environment) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/multi-url-environment ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/no-environment/README.md b/seed/ts-sdk/no-environment/README.md index 01f75be322c..72293557c49 100644 --- a/seed/ts-sdk/no-environment/README.md +++ b/seed/ts-sdk/no-environment/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/no-environment)](https://www.npmjs.com/package/@fern/no-environment) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/no-environment ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/oauth-client-credentials-default/README.md b/seed/ts-sdk/oauth-client-credentials-default/README.md index f6ed75cf7e4..5dd55cd78ac 100644 --- a/seed/ts-sdk/oauth-client-credentials-default/README.md +++ b/seed/ts-sdk/oauth-client-credentials-default/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/oauth-client-credentials-default)](https://www.npmjs.com/package/@fern/oauth-client-credentials-default) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/oauth-client-credentials-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/oauth-client-credentials-environment-variables/README.md b/seed/ts-sdk/oauth-client-credentials-environment-variables/README.md index b14d7e49ba5..05af777364d 100644 --- a/seed/ts-sdk/oauth-client-credentials-environment-variables/README.md +++ b/seed/ts-sdk/oauth-client-credentials-environment-variables/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/oauth-client-credentials-environment-variables)](https://www.npmjs.com/package/@fern/oauth-client-credentials-environment-variables) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/oauth-client-credentials-environment-variables ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/oauth-client-credentials-nested-root/never-throw-errors/README.md b/seed/ts-sdk/oauth-client-credentials-nested-root/never-throw-errors/README.md index 71317b861b6..305796341d2 100644 --- a/seed/ts-sdk/oauth-client-credentials-nested-root/never-throw-errors/README.md +++ b/seed/ts-sdk/oauth-client-credentials-nested-root/never-throw-errors/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/oauth-client-credentials-nested-root)](https://www.npmjs.com/package/@fern/oauth-client-credentials-nested-root) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/oauth-client-credentials-nested-root ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/oauth-client-credentials-nested-root/no-custom-config/README.md b/seed/ts-sdk/oauth-client-credentials-nested-root/no-custom-config/README.md index 71317b861b6..305796341d2 100644 --- a/seed/ts-sdk/oauth-client-credentials-nested-root/no-custom-config/README.md +++ b/seed/ts-sdk/oauth-client-credentials-nested-root/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/oauth-client-credentials-nested-root)](https://www.npmjs.com/package/@fern/oauth-client-credentials-nested-root) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/oauth-client-credentials-nested-root ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/oauth-client-credentials/README.md b/seed/ts-sdk/oauth-client-credentials/README.md index b3c4fd9916a..531ba6dc523 100644 --- a/seed/ts-sdk/oauth-client-credentials/README.md +++ b/seed/ts-sdk/oauth-client-credentials/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/oauth-client-credentials)](https://www.npmjs.com/package/@fern/oauth-client-credentials) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/oauth-client-credentials ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/optional/README.md b/seed/ts-sdk/optional/README.md index ae191361597..3362ab61064 100644 --- a/seed/ts-sdk/optional/README.md +++ b/seed/ts-sdk/optional/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/optional)](https://www.npmjs.com/package/@fern/optional) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/optional ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/package-yml/README.md b/seed/ts-sdk/package-yml/README.md index db774a2f4e9..e4ba649e8a1 100644 --- a/seed/ts-sdk/package-yml/README.md +++ b/seed/ts-sdk/package-yml/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/package-yml)](https://www.npmjs.com/package/@fern/package-yml) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/package-yml ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/pagination/README.md b/seed/ts-sdk/pagination/README.md index c62624e6f4c..aa023340a4d 100644 --- a/seed/ts-sdk/pagination/README.md +++ b/seed/ts-sdk/pagination/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/pagination)](https://www.npmjs.com/package/@fern/pagination) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/pagination ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/plain-text/README.md b/seed/ts-sdk/plain-text/README.md index 2efcbeeb859..f8c57d73798 100644 --- a/seed/ts-sdk/plain-text/README.md +++ b/seed/ts-sdk/plain-text/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/plain-text)](https://www.npmjs.com/package/@fern/plain-text) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/plain-text ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/query-parameters/no-custom-config/README.md b/seed/ts-sdk/query-parameters/no-custom-config/README.md index 379c9b281ce..6a579f9a67e 100644 --- a/seed/ts-sdk/query-parameters/no-custom-config/README.md +++ b/seed/ts-sdk/query-parameters/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/query-parameters)](https://www.npmjs.com/package/@fern/query-parameters) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/query-parameters ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/query-parameters/no-serde-layer-query/README.md b/seed/ts-sdk/query-parameters/no-serde-layer-query/README.md index 379c9b281ce..6a579f9a67e 100644 --- a/seed/ts-sdk/query-parameters/no-serde-layer-query/README.md +++ b/seed/ts-sdk/query-parameters/no-serde-layer-query/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/query-parameters)](https://www.npmjs.com/package/@fern/query-parameters) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/query-parameters ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/reserved-keywords/README.md b/seed/ts-sdk/reserved-keywords/README.md index 91df7d59fae..1adecaf0fa4 100644 --- a/seed/ts-sdk/reserved-keywords/README.md +++ b/seed/ts-sdk/reserved-keywords/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/reserved-keywords)](https://www.npmjs.com/package/@fern/reserved-keywords) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/reserved-keywords ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/response-property/README.md b/seed/ts-sdk/response-property/README.md index c884dbda574..ab6e8bc49be 100644 --- a/seed/ts-sdk/response-property/README.md +++ b/seed/ts-sdk/response-property/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/response-property)](https://www.npmjs.com/package/@fern/response-property) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/response-property ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/simple-fhir/README.md b/seed/ts-sdk/simple-fhir/README.md index d4bae039403..721a2c0788b 100644 --- a/seed/ts-sdk/simple-fhir/README.md +++ b/seed/ts-sdk/simple-fhir/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/simple-fhir)](https://www.npmjs.com/package/@fern/simple-fhir) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/simple-fhir ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/single-url-environment-default/README.md b/seed/ts-sdk/single-url-environment-default/README.md index d94c84344b6..c7bd9c311d5 100644 --- a/seed/ts-sdk/single-url-environment-default/README.md +++ b/seed/ts-sdk/single-url-environment-default/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/single-url-environment-default)](https://www.npmjs.com/package/@fern/single-url-environment-default) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/single-url-environment-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/single-url-environment-no-default/README.md b/seed/ts-sdk/single-url-environment-no-default/README.md index 3bd653204e4..fbfb77a0571 100644 --- a/seed/ts-sdk/single-url-environment-no-default/README.md +++ b/seed/ts-sdk/single-url-environment-no-default/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/single-url-environment-no-default)](https://www.npmjs.com/package/@fern/single-url-environment-no-default) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/single-url-environment-no-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/streaming/allow-custom-fetcher/README.md b/seed/ts-sdk/streaming/allow-custom-fetcher/README.md index aefe0b47576..19b44881e55 100644 --- a/seed/ts-sdk/streaming/allow-custom-fetcher/README.md +++ b/seed/ts-sdk/streaming/allow-custom-fetcher/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/streaming)](https://www.npmjs.com/package/@fern/streaming) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/streaming ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/streaming/no-custom-config/README.md b/seed/ts-sdk/streaming/no-custom-config/README.md index aefe0b47576..19b44881e55 100644 --- a/seed/ts-sdk/streaming/no-custom-config/README.md +++ b/seed/ts-sdk/streaming/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/streaming)](https://www.npmjs.com/package/@fern/streaming) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/streaming ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/trace/exhaustive/README.md b/seed/ts-sdk/trace/exhaustive/README.md index 9d066cba1df..45747a04896 100644 --- a/seed/ts-sdk/trace/exhaustive/README.md +++ b/seed/ts-sdk/trace/exhaustive/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/trace)](https://www.npmjs.com/package/@fern/trace) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/trace ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/trace/no-custom-config/README.md b/seed/ts-sdk/trace/no-custom-config/README.md index dbaa97a4883..77f4b493c75 100644 --- a/seed/ts-sdk/trace/no-custom-config/README.md +++ b/seed/ts-sdk/trace/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/trace)](https://www.npmjs.com/package/@fern/trace) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/trace ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/trace/no-zurg-no-throwing/README.md b/seed/ts-sdk/trace/no-zurg-no-throwing/README.md index dbaa97a4883..77f4b493c75 100644 --- a/seed/ts-sdk/trace/no-zurg-no-throwing/README.md +++ b/seed/ts-sdk/trace/no-zurg-no-throwing/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/trace)](https://www.npmjs.com/package/@fern/trace) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/trace ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/trace/no-zurg-trace/README.md b/seed/ts-sdk/trace/no-zurg-trace/README.md index dbaa97a4883..77f4b493c75 100644 --- a/seed/ts-sdk/trace/no-zurg-trace/README.md +++ b/seed/ts-sdk/trace/no-zurg-trace/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/trace)](https://www.npmjs.com/package/@fern/trace) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/trace ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/ts-express-casing/README.md b/seed/ts-sdk/ts-express-casing/README.md index 3d2515a2c82..99e333773dc 100644 --- a/seed/ts-sdk/ts-express-casing/README.md +++ b/seed/ts-sdk/ts-express-casing/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/ts-express-casing)](https://www.npmjs.com/package/@fern/ts-express-casing) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/ts-express-casing ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/undiscriminated-unions/no-custom-config/README.md b/seed/ts-sdk/undiscriminated-unions/no-custom-config/README.md index 07f7b778352..4a592c2ff15 100644 --- a/seed/ts-sdk/undiscriminated-unions/no-custom-config/README.md +++ b/seed/ts-sdk/undiscriminated-unions/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/undiscriminated-unions)](https://www.npmjs.com/package/@fern/undiscriminated-unions) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/undiscriminated-unions ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/undiscriminated-unions/skip-response-validation/README.md b/seed/ts-sdk/undiscriminated-unions/skip-response-validation/README.md index 07f7b778352..4a592c2ff15 100644 --- a/seed/ts-sdk/undiscriminated-unions/skip-response-validation/README.md +++ b/seed/ts-sdk/undiscriminated-unions/skip-response-validation/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/undiscriminated-unions)](https://www.npmjs.com/package/@fern/undiscriminated-unions) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/undiscriminated-unions ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/unions/README.md b/seed/ts-sdk/unions/README.md index e6bf46455f3..52a8f221af9 100644 --- a/seed/ts-sdk/unions/README.md +++ b/seed/ts-sdk/unions/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/unions)](https://www.npmjs.com/package/@fern/unions) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/unions ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/unknown/no-custom-config/README.md b/seed/ts-sdk/unknown/no-custom-config/README.md index 9b88486d1a8..d4d8b27630e 100644 --- a/seed/ts-sdk/unknown/no-custom-config/README.md +++ b/seed/ts-sdk/unknown/no-custom-config/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/unknown)](https://www.npmjs.com/package/@fern/unknown) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/unknown ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/unknown/unknown-as-any/README.md b/seed/ts-sdk/unknown/unknown-as-any/README.md index 9b88486d1a8..d4d8b27630e 100644 --- a/seed/ts-sdk/unknown/unknown-as-any/README.md +++ b/seed/ts-sdk/unknown/unknown-as-any/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/unknown)](https://www.npmjs.com/package/@fern/unknown) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/unknown ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/validation/README.md b/seed/ts-sdk/validation/README.md index 56c0e8be483..790c8df7268 100644 --- a/seed/ts-sdk/validation/README.md +++ b/seed/ts-sdk/validation/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/validation)](https://www.npmjs.com/package/@fern/validation) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/validation ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/variables/README.md b/seed/ts-sdk/variables/README.md index d3e1c7be104..52348d76b77 100644 --- a/seed/ts-sdk/variables/README.md +++ b/seed/ts-sdk/variables/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/variables)](https://www.npmjs.com/package/@fern/variables) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/variables ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/version-no-default/README.md b/seed/ts-sdk/version-no-default/README.md index bb5d0cda44b..44e93f8bf52 100644 --- a/seed/ts-sdk/version-no-default/README.md +++ b/seed/ts-sdk/version-no-default/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/version-no-default)](https://www.npmjs.com/package/@fern/version-no-default) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/version-no-default ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: diff --git a/seed/ts-sdk/version/README.md b/seed/ts-sdk/version/README.md index d7478d9b3f5..58b6373c5a1 100644 --- a/seed/ts-sdk/version/README.md +++ b/seed/ts-sdk/version/README.md @@ -1,6 +1,6 @@ # Seed TypeScript Library -[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FTypeScript) [![npm shield](https://img.shields.io/npm/v/@fern/version)](https://www.npmjs.com/package/@fern/version) The Seed TypeScript library provides convenient access to the Seed API from TypeScript. @@ -11,6 +11,10 @@ The Seed TypeScript library provides convenient access to the Seed API from Type npm i -s @fern/version ``` +## Reference + +A full reference for this library is available [here](./reference.md). + ## Usage Instantiate and use the client with the following: