{"id":17298445,"url":"https://github.com/kamranayub/typeform-dotnet","last_synced_at":"2025-07-08T04:04:28.348Z","repository":{"id":45000421,"uuid":"440879600","full_name":"kamranayub/typeform-dotnet","owner":"kamranayub","description":"A .NET standard 2.0 wrapper around the official Typeform API. Currently a work-in-progress, see Supported Endpoints.","archived":false,"fork":false,"pushed_at":"2023-12-04T05:24:51.000Z","size":165,"stargazers_count":1,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-24T19:34:59.395Z","etag":null,"topics":["api","api-client","client","dotnet","netstandard","sdk","typeform","typeform-api","typeform-client"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kamranayub.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"kamranayub"}},"created_at":"2021-12-22T14:11:20.000Z","updated_at":"2022-02-08T01:56:03.000Z","dependencies_parsed_at":"2024-10-15T11:19:06.877Z","dependency_job_id":"a9ab2fcf-96de-41c6-b0ce-9418cc0b76a3","html_url":"https://github.com/kamranayub/typeform-dotnet","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/kamranayub/typeform-dotnet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamranayub%2Ftypeform-dotnet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamranayub%2Ftypeform-dotnet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamranayub%2Ftypeform-dotnet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamranayub%2Ftypeform-dotnet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kamranayub","download_url":"https://codeload.github.com/kamranayub/typeform-dotnet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamranayub%2Ftypeform-dotnet/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264192226,"owners_count":23570736,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-client","client","dotnet","netstandard","sdk","typeform","typeform-api","typeform-client"],"created_at":"2024-10-15T11:19:04.321Z","updated_at":"2025-07-08T04:04:28.330Z","avatar_url":"https://github.com/kamranayub.png","language":"C#","funding_links":["https://github.com/sponsors/kamranayub"],"categories":[],"sub_categories":[],"readme":"# typeform-dotnet\n\n[![.NET](https://github.com/kamranayub/typeform-dotnet/actions/workflows/dotnet.yml/badge.svg)](https://github.com/kamranayub/typeform-dotnet/actions/workflows/dotnet.yml) [![Nuget](https://img.shields.io/nuget/v/Typeform)](https://nuget.org/packages/Typeform) [![Nuget](https://img.shields.io/nuget/dt/Typeform?label=nuget%20downloads)](https://nuget.org/packages/Typeform)\n\nA .NET Standard 2.0 SDK wrapper built with [Refit](https://github.com/reactiveui/refit) around Typeform's API.\n\n## Twitch Streams\n\nThis SDK is (mostly) built in public on Twitch. Watch the collection here:\n\nhttps://www.twitch.tv/collections/Lm3OF4Q0xBZaSA\n\n## Supported Endpoints\n\n- [Retrieve responses](https://developer.typeform.com/responses/reference/retrieve-responses/)\n- [Retrieve response file](https://developer.typeform.com/responses/reference/retrieve-response-file/)\n- [Delete responses](https://developer.typeform.com/responses/reference/delete-responses/)\n\n\n## Install\n\nvia [Nuget](https://nuget.org/packages/Typeform)\n\n```sh\n# Package Manager\nInstall-Package Typeform\n\n# dotnet\ndotnet add package Typeform\n```\n\n## Usage\n\n### Create API client\n\nTo create an instance of the Refit `ITypeformApi` client:\n\n```c#\nusing Typeform;\n\nvar client = TypeformClient.CreateApi();\n```\n\n### .NET Service Injection\n\n```c#\nusing Typeform;\n\n// TODO: Not Implemented Yet\n// services.AddTypeformApi();\n\n// You can always do this\nservices.AddRefitClient\u003cITypeformApi\u003e(TypeformClient.DefaultSettings);\n```\n\n\n### Other DI Containers\n\n```c#\nusing Typeform;\n\n// Ninject\nkernel.Bind\u003cITypeformApi\u003e().ToMethod(ctx =\u003e TypeformClient.CreateApi()).InSingletonScope();\n```\n\n### Customizing Refit Settings\n\n`TypeformClient.DefaultSettings` contains the default Refit settings used for the API. You can create new derived settings to pass if you need to through the `CreateApi` static method.\n\n`TypeformClient.DefaultSystemTextJsonSerializerOptions` contains the default `System.Text.Json` serializer options. This handles naming policy and JSON deserialization according to the requirements of the Typeform API.\n\n### Consuming the API\n\nYou will need to pass your Typeform OAuth or Personal Access Token. Currently, this\nis implemented as a string argument to the endpoint methods.\n\n\u003e TODO: Obtaining an OAuth token is not implemented yet. But for server-side flows,\n\u003e usually a Personal Access Token is \"good enough.\"\n\n```c#\npublic class HomeController : Controller {\n  private readonly IConfiguration _configuration;\n  private readonly ITypeformApi _typeformApi;\n\n  public Controller(IConfiguration configuration, ITypeformApi typeformApi) {\n    _configuration = configuration;\n    _typeformApi = typeformApi;\n  }\n\n  public async Task\u003cActionResult\u003e Index() {\n    var accessToken = _configuration[\"TypeformAccessToken\"]\n    var formId = \"abc123\";\n\n    var responses = await _typeformApi.GetFormResponsesAsync(accessToken, formId);\n  }\n}\n```\n\n# Responses API\n\n## Retrieve Responses\n\nThe Typeform Responses API returns form responses that include answers. Each answer can be a different type and to accomodate this, the SDK deserializes into different class implementations based on the answer `type` discriminator.\n\n### Retrieving an answer by type\n\nIf you know what type an answer is _supposed_ to be, you can use the `Answers.GetAnswer\u003cT\u003e(index)` method\nto retrieve an answer at an index that is the expected type:\n\n**Answer By Index**\n\n```c#\nvar responses = await _typeformApi.GetFormResponsesAsync(accessToken, formId);\n\n// Retrieve first response's answer as Text type (by index)\nvar answerText = responses.Items[0].Answers.GetAnswer\u003cTypeformAnswerText\u003e(0);\n```\n\n**Answer By Field ID**\n\n```c#\nvar responses = await _typeformApi.GetFormResponsesAsync(accessToken, formId);\n\n// Retrieve first response's answer as Text type (by field.id)\nvar answerText = responses.Items[0].Answers.GetAnswerById\u003cTypeformAnswerText\u003e(\"abc123\");\n```\n\n**Answer By Field Ref**\n\n```c#\nvar responses = await _typeformApi.GetFormResponsesAsync(accessToken, formId);\n\n// Retrieve first response's answer as Text type (by field.ref)\nvar answerText = responses.Items[0].Answers.GetAnswerByRef\u003cTypeformAnswerText\u003e(\"my_custom_ref\");\n```\n\nIf you _do not_ know what type an answer is _supposed_ to be, you can inspect its type:\n\n```c#\nvar responses = await _typeformApi.GetFormResponsesAsync(accessToken, formId);\n\n// Retrieve first response's answer type\nvar firstAnswerType = responses.Items[0].Answers[0].Type;\n\nif (firstAnswerType == TypeformAnswerType.Text) {\n  var firstAnswer = responses.Items[0].Answers[0] as TypeformAnswerText;\n}\n```\n\nBased on Typeform's response structure, it's not easily possible to get static typing of the answers\nwithout knowing the type in advance.\n\n### Answer type mapping\n\nFor reference, this is the mapping for each answer type used:\n\n```c#\n// Get the answer type enum value\nvar type = answer.Type;\n\n// Determine the class type to map to\nType answerInstanceType = type switch\n{\n  TypeformAnswerType.Boolean =\u003e typeof(TypeformAnswerBoolean),\n  TypeformAnswerType.Choice =\u003e typeof(TypeformAnswerChoice),\n  TypeformAnswerType.Choices =\u003e typeof(TypeformAnswerChoices),\n  TypeformAnswerType.Date =\u003e typeof(TypeformAnswerDate),\n  TypeformAnswerType.Email =\u003e typeof(TypeformAnswerEmail),\n  TypeformAnswerType.FileUrl =\u003e typeof(TypeformAnswerFileUrl),\n  TypeformAnswerType.Number =\u003e typeof(TypeformAnswerNumber),\n  TypeformAnswerType.Payment =\u003e typeof(TypeformAnswerPayment),\n  TypeformAnswerType.Text =\u003e typeof(TypeformAnswerText),\n  TypeformAnswerType.Url =\u003e typeof(TypeformAnswerUrl),\n  _ =\u003e typeof(TypeformAnswer)\n};\n```\n\nThe SDK deserialization takes care of deserializing to the correct type so you can safely cast it.\n\n### Retrieving a variable's type\n\nA form variables collection is similar to answers. You can use the same pattern to get variables by type.\n\n**Variables By Index**\n\n```c#\nvar responses = await _typeformApi.GetFormResponsesAsync(accessToken, formId);\n\n// Retrieve first response's variable (by index)\nvar answerText = responses.Items[0].Variables.GetVariable\u003cTypeformVariableText\u003e(0);\n```\n\n**Variables By Key**\n\n```c#\nvar responses = await _typeformApi.GetFormResponsesAsync(accessToken, formId);\n\n// Retrieve first response's variable (by key)\nvar answerText = responses.Items[0].Variables.GetVariable\u003cTypeformVariableText\u003e(\"name\");\n```\n\n## Delete Responses\n\nUse `ITypeformApi.DeleteResponsesAsync()` and pass a list of response IDs to delete.\n\n## Retrieve Response File\n\nUploaded files to a form can be downloaded via the REST API. \n\n### Retrieve file by `file_url`\n\nThe [Typeform docs](https://developer.typeform.com/responses/JSON-response-explanation/) specify that you **cannot** rely on the values of `file_url` in Form Responses to have a consistent structure.\n\nHowever, I have found that many `file_url` values _do match_ the REST endpoint path value. To accommodate this, I've added an extension method `GetFormResponseFileStreamFromUrlAsync` which you can use to pass a `FileUrl` value directly and attempt to download a file.\n\n```c#\nITypeformApi typeformApi = TypeformClient.CreateApi();\n\nvar responses = await typeformApi.GetFormResponsesAsync(accessToken, formId);\nvar uploadFileAnswer = responses.Items[0].Answers.GetAnswerByRef\u003cTypeformAnswerFileUrl\u003e(\"my_custom_upload_ref\");\n\nApiResponse\u003cStream\u003e fileResponse = await typeformApi.GetFormResponseFileStreamFromUrlAsync(\n  accessToken,\n  uploadFileAnswer.FileUrl\n);\n\nvar contents = await fileResponse.ReadAllBytesAsync(/* chunkSize: \u003coptional value in bytes\u003e */);\n\nawait System.IO.File.WriteAllBytesAsync(filename, contents);\n```\n\n### Retrieve file by parameters\n\nYou can also manually specify the `form_id`, `response_id`, `field_id` and `filename` to download\nusing `ITypeformApi.GetFormResponseFileStreamAsync()`.\n\nThe return value of this method is a Refit `ApiResponse\u003cStream\u003e` and you can manipulate the `Stream` response any way\nyou see fit. There is a `ReadAllBytesAsync()` extension method that will read the full bytes using a chunked buffer:\n\n```c#\nITypeformApi typeformApi = TypeformClient.CreateApi();\n\nApiResponse\u003cStream\u003e fileResponse = await typeformApi.GetFormResponseFileStreamAsync(\n  accessToken,\n  formId,\n  responseId,\n  fieldId,\n  filename\n);\n\nvar contents = await fileResponse.ReadAllBytesAsync(/* chunkSize: \u003coptional value in bytes\u003e */);\n\nawait System.IO.File.WriteAllBytesAsync(filename, contents);\n```\n\nIf you need to download a file using a `FileUrl` value from the Form Responses API, you will need to construct your own `HttpClient` to download it [like this example](https://dev.to/1001binary/download-file-using-httpclient-wrapper-asynchronously-1p6).\n\n## TODO\n\n- [x] Create a basic API client\n- [x] Support for passing in an access token\n- [x] Nuget package flow\n- [x] Github CI for tests / build / publish\n- [x] Target .NET Standard / maximize compatibility\n- [ ] Support for [Responses API](https://developer.typeform.com/responses/)\n  - [x] [Retrieve responses](https://developer.typeform.com/responses/reference/retrieve-responses/)\n  - [x] [Delete responses](https://developer.typeform.com/responses/reference/delete-responses/)\n  - [x] [Retrieve response file](https://developer.typeform.com/responses/reference/retrieve-response-file/)\n  - [ ] [Retrieve Form Insights](https://developer.typeform.com/responses/reference/retrieve-form-insights/)\n- [ ] Support for [Webhooks API](https://developer.typeform.com/webhooks/)\n- [ ] Support for [Create API](https://developer.typeform.com/create/)\n- [ ] Support OAuth flow to obtain access token\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkamranayub%2Ftypeform-dotnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkamranayub%2Ftypeform-dotnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkamranayub%2Ftypeform-dotnet/lists"}